Index: lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp +++ lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -29,7 +29,9 @@ class BlockInCriticalSectionChecker : public Checker { - 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 BlockInCritSectionBugType; @@ -40,6 +42,13 @@ public: BlockInCriticalSectionChecker(); + DefaultBool C11Enabled; + DefaultBool PthreadEnabled; + + 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; /// Process unlock. @@ -55,34 +64,69 @@ BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() : 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. BlockInCritSectionBugType.reset( new BugType(this, "Call to blocking function in critical section", "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) + || (PthreadEnabled && Call.isCalled(PthreadLockFn)) + || (PthreadEnabled && Call.isCalled(PthreadTryLockFn)) + || (C11Enabled && Call.isCalled(MtxLock)) + || (C11Enabled && Call.isCalled(MtxTimedLock)) + || (C11Enabled && Call.isCalled(MtxTryLock))) { + return true; + } + return false; +} + +bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const { + if (Call.isCalled(UnlockFn) + || (PthreadEnabled && Call.isCalled(PthreadUnlockFn)) + || (C11Enabled && Call.isCalled(MtxUnlock))) { + return true; + } + return false; +} + void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { } void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { - if (!Call.isCalled(LockFn) - && !Call.isCalled(SleepFn) - && !Call.isCalled(GetcFn) - && !Call.isCalled(FgetsFn) - && !Call.isCalled(ReadFn) - && !Call.isCalled(RecvFn) - && !Call.isCalled(UnlockFn)) + if (!isBlockingFunction(Call) + && !isLockFunction(Call) + && !isUnlockFunction(Call)) return; ProgramStateRef State = C.getState(); unsigned mutexCount = State->get(); - if (Call.isCalled(UnlockFn) && mutexCount > 0) { + if (isUnlockFunction(Call) && mutexCount > 0) { State = State->set(--mutexCount); C.addTransition(State); - } else if (Call.isCalled(LockFn)) { + } else if (isLockFunction(Call)) { State = State->set(++mutexCount); C.addTransition(State); } else if (mutexCount > 0) { @@ -105,5 +149,12 @@ } void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { - mgr.registerChecker(); + BlockInCriticalSectionChecker *checker = + mgr.registerChecker(); + + checker->C11Enabled = + mgr.getAnalyzerOptions().getBooleanOption("C11Enabled", true, checker); + + checker->PthreadEnabled = + mgr.getAnalyzerOptions().getBooleanOption("PthreadEnabled", true, checker); } Index: test/Analysis/block-in-critical-section-c11-disabled.cpp =================================================================== --- /dev/null +++ test/Analysis/block-in-critical-section-c11-disabled.cpp @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.unix.BlockInCriticalSection -std=c++11 -verify -analyzer-config alpha.unix.BlockInCriticalSection:C11Enabled=false %s + +void sleep(int x) {} + +namespace std { +struct mutex { + void lock() {} + void unlock() {} +}; +} + +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; + m.lock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + m.unlock(); +} + +void testBlockInCriticalSectionWithPthreadMutex() { + pthread_mutex_lock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + pthread_mutex_unlock(); + + pthread_mutex_trylock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + pthread_mutex_unlock(); +} + +void testBlockInCriticalSectionC11Locks() { + mtx_lock(); + sleep(3); // no-warning + getc(); // no-warning + fgets(); // no-warning + read(); // no-warning + recv(); // no-warning + mtx_unlock(); + + mtx_timedlock(); + sleep(3); // no-warning + getc(); // no-warning + fgets(); // no-warning + read(); // no-warning + recv(); // no-warning + mtx_unlock(); + + mtx_trylock(); + sleep(3); // no-warning + getc(); // no-warning + fgets(); // no-warning + read(); // no-warning + recv(); // no-warning + mtx_unlock(); +} + +void testBlockInCriticalSectionWithNestedMutexes() { + std::mutex m, n, k; + m.lock(); + n.lock(); + k.lock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + k.unlock(); + sleep(5); // expected-warning {{A blocking function %s is called inside a critical section}} + n.unlock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + m.unlock(); + sleep(3); // no-warning +} + +void f() { + sleep(1000); // expected-warning {{A blocking function %s is called inside a critical section}} +} + +void testBlockInCriticalSectionInterProcedural() { + std::mutex m; + m.lock(); + f(); + m.unlock(); +} + +void testBlockInCriticalSectionUnexpectedUnlock() { + std::mutex m; + m.unlock(); + sleep(1); // no-warning + m.lock(); + sleep(1); // expected-warning {{A blocking function %s is called inside a critical section}} +} Index: test/Analysis/block-in-critical-section-pthread-disabled.cpp =================================================================== --- /dev/null +++ test/Analysis/block-in-critical-section-pthread-disabled.cpp @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.unix.BlockInCriticalSection -std=c++11 -verify -analyzer-config alpha.unix.BlockInCriticalSection:PthreadEnabled=false %s + +void sleep(int x) {} + +namespace std { +struct mutex { + void lock() {} + void unlock() {} +}; +} + +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; + m.lock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + m.unlock(); +} + +void testBlockInCriticalSectionWithPthreadMutex() { + pthread_mutex_lock(); + sleep(3); // no-warning + getc(); // no-warning + fgets(); // no-warning + read(); // no-warning + recv(); // no-warning + pthread_mutex_unlock(); + + pthread_mutex_trylock(); + sleep(3); // no-warning + getc(); // no-warning + fgets(); // no-warning + read(); // no-warning + recv(); // no-warning + pthread_mutex_unlock(); +} + +void testBlockInCriticalSectionC11Locks() { + mtx_lock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + mtx_unlock(); + + mtx_timedlock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + mtx_unlock(); + + mtx_trylock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + mtx_unlock(); +} + +void testBlockInCriticalSectionWithNestedMutexes() { + std::mutex m, n, k; + m.lock(); + n.lock(); + k.lock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + k.unlock(); + sleep(5); // expected-warning {{A blocking function %s is called inside a critical section}} + n.unlock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + m.unlock(); + sleep(3); // no-warning +} + +void f() { + sleep(1000); // expected-warning {{A blocking function %s is called inside a critical section}} +} + +void testBlockInCriticalSectionInterProcedural() { + std::mutex m; + m.lock(); + f(); + m.unlock(); +} + +void testBlockInCriticalSectionUnexpectedUnlock() { + std::mutex m; + m.unlock(); + sleep(1); // no-warning + m.lock(); + sleep(1); // expected-warning {{A blocking function %s is called inside a critical section}} +} Index: test/Analysis/block-in-critical-section.cpp =================================================================== --- test/Analysis/block-in-critical-section.cpp +++ test/Analysis/block-in-critical-section.cpp @@ -9,13 +9,75 @@ }; } -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; m.lock(); sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} m.unlock(); } +void testBlockInCriticalSectionWithPthreadMutex() { + pthread_mutex_lock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + pthread_mutex_unlock(); + + pthread_mutex_trylock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + pthread_mutex_unlock(); +} + +void testBlockInCriticalSectionC11Locks() { + mtx_lock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + mtx_unlock(); + + mtx_timedlock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + mtx_unlock(); + + mtx_trylock(); + sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}} + getc(); // expected-warning {{A blocking function %s is called inside a critical section}} + fgets(); // expected-warning {{A blocking function %s is called inside a critical section}} + read(); // expected-warning {{A blocking function %s is called inside a critical section}} + recv(); // expected-warning {{A blocking function %s is called inside a critical section}} + mtx_unlock(); +} + void testBlockInCriticalSectionWithNestedMutexes() { std::mutex m, n, k; m.lock(); Index: www/analyzer/alpha_checks.html =================================================================== --- www/analyzer/alpha_checks.html +++ www/analyzer/alpha_checks.html @@ -910,6 +910,31 @@ } + + + +