The checker knows that `std::lock_guard` and `std::unique_lock` introduce critical sections, but doesn't know about the newer `std::scoped_lock`.
340 lines
17 KiB
C++
340 lines
17 KiB
C++
// RUN: %clang_analyze_cc1 \
|
|
// RUN: -analyzer-checker=unix.BlockInCriticalSection \
|
|
// RUN: -std=c++11 \
|
|
// RUN: -analyzer-output text \
|
|
// RUN: -verify %s
|
|
|
|
void sleep(int x) {}
|
|
|
|
namespace std {
|
|
struct mutex {
|
|
void lock() {}
|
|
void unlock() {}
|
|
};
|
|
template<typename T>
|
|
struct lock_guard {
|
|
lock_guard<T>(std::mutex) {}
|
|
~lock_guard<T>() {}
|
|
};
|
|
struct defer_lock_t {};
|
|
constexpr defer_lock_t defer_lock{};
|
|
template<typename T>
|
|
struct unique_lock {
|
|
unique_lock<T>(std::mutex) {}
|
|
unique_lock<T>(std::mutex, defer_lock_t) {} // defer_lock parameter
|
|
~unique_lock<T>() {}
|
|
};
|
|
template<typename T>
|
|
struct not_real_lock {
|
|
not_real_lock<T>(std::mutex) {}
|
|
};
|
|
|
|
template<typename... MutexTypes>
|
|
struct scoped_lock {
|
|
explicit scoped_lock(MutexTypes&...) {}
|
|
~scoped_lock() {}
|
|
};
|
|
} // namespace std
|
|
|
|
struct FILE;
|
|
int getc(FILE *stream);
|
|
char* fgets(char *str, FILE *stream);
|
|
using ssize_t = long long;
|
|
using size_t = unsigned long long;
|
|
ssize_t read(int fd, void *buf, size_t count);
|
|
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
|
|
|
|
struct pthread_mutex_t;
|
|
int pthread_mutex_lock(pthread_mutex_t *mutex);
|
|
int pthread_mutex_trylock(pthread_mutex_t *mutex);
|
|
int pthread_mutex_unlock(pthread_mutex_t *mutex);
|
|
|
|
struct mtx_t;
|
|
int mtx_lock(mtx_t *mutex);
|
|
int mtx_timedlock(mtx_t *mutex);
|
|
int mtx_trylock(mtx_t *mutex);
|
|
int mtx_unlock(mtx_t *mutex);
|
|
|
|
// global params for dummy function calls
|
|
FILE *stream;
|
|
char *str;
|
|
int fd;
|
|
void *buf;
|
|
size_t count;
|
|
int sockfd;
|
|
size_t len;
|
|
int flags;
|
|
|
|
void testBlockInCriticalSectionWithStdMutex() {
|
|
std::mutex m;
|
|
m.lock(); // expected-note 5{{Entering critical section here}}
|
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'getc' inside of critical section}}
|
|
fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'fgets' inside of critical section}}
|
|
read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'read' inside of critical section}}
|
|
recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'recv' inside of critical section}}
|
|
m.unlock();
|
|
}
|
|
|
|
void testBlockInCriticalSectionWithPthreadMutex(pthread_mutex_t *mutex) {
|
|
pthread_mutex_lock(mutex); // expected-note 5{{Entering critical section here}}
|
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'getc' inside of critical section}}
|
|
fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'fgets' inside of critical section}}
|
|
read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'read' inside of critical section}}
|
|
recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'recv' inside of critical section}}
|
|
pthread_mutex_unlock(mutex);
|
|
|
|
pthread_mutex_trylock(mutex); // expected-note 5{{Entering critical section here}}
|
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'getc' inside of critical section}}
|
|
fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'fgets' inside of critical section}}
|
|
read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'read' inside of critical section}}
|
|
recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'recv' inside of critical section}}
|
|
pthread_mutex_unlock(mutex);
|
|
}
|
|
|
|
void testBlockInCriticalSectionC11Locks(mtx_t *mutex) {
|
|
mtx_lock(mutex); // expected-note 5{{Entering critical section here}}
|
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'getc' inside of critical section}}
|
|
fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'fgets' inside of critical section}}
|
|
read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'read' inside of critical section}}
|
|
recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'recv' inside of critical section}}
|
|
mtx_unlock(mutex);
|
|
|
|
mtx_timedlock(mutex); // expected-note 5{{Entering critical section here}}
|
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'getc' inside of critical section}}
|
|
fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'fgets' inside of critical section}}
|
|
read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'read' inside of critical section}}
|
|
recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'recv' inside of critical section}}
|
|
mtx_unlock(mutex);
|
|
|
|
mtx_trylock(mutex); // expected-note 5{{Entering critical section here}}
|
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'getc' inside of critical section}}
|
|
fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'fgets' inside of critical section}}
|
|
read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'read' inside of critical section}}
|
|
recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'recv' inside of critical section}}
|
|
mtx_unlock(mutex);
|
|
}
|
|
|
|
void testMultipleBlockingCalls() {
|
|
std::mutex m;
|
|
m.lock(); // expected-note 1{{Entering critical section here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
m.unlock();
|
|
sleep(2); // no-warning
|
|
}
|
|
|
|
void testMultipleMutexesMultipleBlockingCalls() {
|
|
std::mutex m, n, k;
|
|
m.lock(); // expected-note 2{{Entering critical section here}}
|
|
n.lock(); // expected-note 2{{Entering critical section here}}
|
|
k.lock(); // expected-note 1{{Entering critical section here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
k.unlock();
|
|
sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
}
|
|
|
|
|
|
void testRecursiveAcquisition() {
|
|
std::mutex m;
|
|
m.lock(); // expected-note {{Entering critical section for the 1st time here}}
|
|
m.lock(); // expected-note {{Entering critical section for the 2nd time here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
m.unlock();
|
|
m.unlock();
|
|
}
|
|
|
|
void testRecursiveAcquisitionWithMultipleBlockingCalls() {
|
|
std::mutex m;
|
|
m.lock(); // expected-note 1{{Entering critical section for the 1st time here}}
|
|
// expected-note@-1 {{Entering critical section here}}
|
|
m.lock(); // expected-note 1{{Entering critical section for the 2nd time here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
m.unlock();
|
|
// this next 'sleep' call is only in the critical section of the first lock
|
|
sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
m.unlock();
|
|
}
|
|
|
|
void testRecursiveAcquisitionWithMultipleMutexes() {
|
|
std::mutex m, n;
|
|
m.lock(); // expected-note 1{{Entering critical section here}}
|
|
n.lock(); // expected-note 2{{Entering critical section here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
m.unlock();
|
|
// this next 'sleep' call is only in the critical section of mutex 'n'
|
|
sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
n.unlock();
|
|
}
|
|
|
|
|
|
void testNestedMutexes() {
|
|
std::mutex m, n, k;
|
|
m.lock(); // expected-note 3{{Entering critical section here}}
|
|
n.lock(); // expected-note 2{{Entering critical section here}}
|
|
k.lock(); // expected-note 1{{Entering critical section here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
k.unlock();
|
|
sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
n.unlock();
|
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
m.unlock();
|
|
sleep(4); // no-warning
|
|
}
|
|
|
|
void testNonOverlappingMutexes() {
|
|
std::mutex m;
|
|
m.lock(); // There should be no warning here
|
|
m.unlock();
|
|
m.lock(); // expected-note {{Entering critical section here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
m.unlock();
|
|
}
|
|
|
|
void testMixedMutexLocksWithIntermittentUnlock() {
|
|
std::mutex m, n, k;
|
|
m.lock(); // expected-note {{Entering critical section here}}
|
|
n.lock(); // the problem is not is this lock's critical section
|
|
n.unlock();
|
|
k.lock(); // same as for n.lock()
|
|
k.unlock();
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
m.unlock();
|
|
}
|
|
|
|
void f() {
|
|
sleep(1000); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
}
|
|
|
|
void testBlockInCriticalSectionInterProcedural() {
|
|
std::mutex m;
|
|
m.lock(); // expected-note {{Entering critical section here}}
|
|
f(); // expected-note {{Calling 'f'}}
|
|
m.unlock();
|
|
}
|
|
|
|
void unknown_function_that_may_lock(std::mutex &);
|
|
void testBlockInCriticalSectionUnexpectedUnlock() {
|
|
std::mutex m;
|
|
unknown_function_that_may_lock(m);
|
|
m.unlock();
|
|
sleep(1); // no-warning
|
|
m.lock(); // expected-note {{Entering critical section here}}
|
|
sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
}
|
|
|
|
void testBlockInCriticalSectionLockGuard() {
|
|
std::mutex g_mutex;
|
|
std::not_real_lock<std::mutex> not_real_lock(g_mutex);
|
|
sleep(1); // no-warning
|
|
|
|
std::lock_guard<std::mutex> lock(g_mutex); // expected-note {{Entering critical section here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
}
|
|
|
|
void testBlockInCriticalSectionLockGuardNested() {
|
|
testBlockInCriticalSectionLockGuard(); // expected-note {{Calling 'testBlockInCriticalSectionLockGuard'}}
|
|
sleep(1); // no-warning
|
|
}
|
|
|
|
void testBlockInCriticalSectionUniqueLock() {
|
|
std::mutex g_mutex;
|
|
std::not_real_lock<std::mutex> not_real_lock(g_mutex);
|
|
sleep(1); // no-warning
|
|
|
|
std::unique_lock<std::mutex> lock(g_mutex); // expected-note {{Entering critical section here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
}
|
|
|
|
void testBlockInCriticalSectionUniqueLockNested() {
|
|
testBlockInCriticalSectionUniqueLock(); // expected-note {{Calling 'testBlockInCriticalSectionUniqueLock'}}
|
|
sleep(1); // no-warning
|
|
}
|
|
|
|
void testBlockInCriticalSectionScopedLock() {
|
|
std::mutex m1;
|
|
std::mutex m2;
|
|
std::scoped_lock<std::mutex, std::mutex> lock(m1, m2); // expected-note {{Entering critical section here}}
|
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
}
|
|
|
|
void testBlockInCriticalSectionScopedLockNested() {
|
|
testBlockInCriticalSectionScopedLock(); // expected-note {{Calling 'testBlockInCriticalSectionScopedLock'}}
|
|
sleep(1); // no-warning
|
|
}
|
|
|
|
void testTrylockCurrentlyFalsePositive(pthread_mutex_t *m) {
|
|
// expected-note@+4 {{Assuming the condition is true}}
|
|
// expected-note@+3 {{Taking true branch}}
|
|
// expected-note@+2 {{Assuming the condition is false}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (pthread_mutex_trylock(m) == 0) { // expected-note 2 {{Entering critical section here}}
|
|
// FIXME: we are entering the critical section only in the true branch
|
|
sleep(10); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
pthread_mutex_unlock(m);
|
|
} else {
|
|
sleep(10); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
|
// expected-note@-1 {{Call to blocking function 'sleep' inside of critical section}}
|
|
// FIXME: this is a false positive, the lock was not acquired
|
|
}
|
|
}
|
|
|
|
void testBlockInCriticalSectionUniqueLockWithDeferLock() {
|
|
std::mutex g_mutex;
|
|
std::unique_lock<std::mutex> lock(g_mutex, std::defer_lock);
|
|
sleep(1); // no-warning
|
|
}
|