[analyzer] PthreadLock: Implement mutex escaping.

Differential Revision: https://reviews.llvm.org/D37812
This commit is contained in:
Artem Dergachev 2020-01-24 18:34:04 +03:00
parent 152bc7ffdc
commit dd22be1e3d
3 changed files with 73 additions and 1 deletions

View File

@ -66,7 +66,8 @@ public:
};
class PthreadLockChecker
: public Checker<check::PostCall, check::DeadSymbols> {
: public Checker<check::PostCall, check::DeadSymbols,
check::RegionChanges> {
BugType BT_doublelock{this, "Double locking", "Lock checker"},
BT_doubleunlock{this, "Double unlocking", "Lock checker"},
BT_destroylock{this, "Use destroyed lock", "Lock checker"},
@ -155,6 +156,11 @@ class PthreadLockChecker
public:
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
ProgramStateRef
checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
const LocationContext *LCtx, const CallEvent *Call) const;
void printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) const override;
};
@ -546,6 +552,42 @@ void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
C.addTransition(State);
}
ProgramStateRef PthreadLockChecker::checkRegionChanges(
ProgramStateRef State, const InvalidatedSymbols *Symbols,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
const CallEvent *Call) const {
bool IsLibraryFunction = false;
if (Call && Call->isGlobalCFunction()) {
// Avoid invalidating mutex state when a known supported function is called.
if (Callbacks.lookup(*Call))
return State;
if (Call->isInSystemHeader())
IsLibraryFunction = true;
}
for (auto R : Regions) {
// We assume that system library function wouldn't touch the mutex unless
// it takes the mutex explicitly as an argument.
// FIXME: This is a bit quadratic.
if (IsLibraryFunction &&
std::find(ExplicitRegions.begin(), ExplicitRegions.end(), R) ==
ExplicitRegions.end())
continue;
State = State->remove<LockMap>(R);
State = State->remove<DestroyRetVal>(R);
// TODO: We need to invalidate the lock stack as well. This is tricky
// to implement correctly and efficiently though, because the effects
// of mutex escapes on lock order may be fairly varied.
}
return State;
}
void ento::registerPthreadLockChecker(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockChecker>();
}

View File

@ -21,6 +21,9 @@ typedef struct {
typedef pthread_mutex_t lck_mtx_t;
extern void fake_system_function();
extern void fake_system_function_that_takes_a_mutex(pthread_mutex_t *mtx);
extern int pthread_mutex_lock(pthread_mutex_t *);
extern int pthread_mutex_unlock(pthread_mutex_t *);
extern int pthread_mutex_trylock(pthread_mutex_t *);

View File

@ -221,6 +221,27 @@ void ok29(void) {
lck_rw_unlock_exclusive(&rw); // no-warning
}
void escape_mutex(pthread_mutex_t *m);
void ok30(void) {
pthread_mutex_t local_mtx;
pthread_mutex_init(&local_mtx, NULL);
pthread_mutex_lock(&local_mtx);
escape_mutex(&local_mtx);
pthread_mutex_lock(&local_mtx); // no-warning
pthread_mutex_unlock(&local_mtx);
pthread_mutex_destroy(&local_mtx);
}
void ok31(void) {
pthread_mutex_t local_mtx;
pthread_mutex_init(&local_mtx, NULL);
pthread_mutex_lock(&local_mtx);
fake_system_function_that_takes_a_mutex(&local_mtx);
pthread_mutex_lock(&local_mtx); // no-warning
pthread_mutex_unlock(&local_mtx);
pthread_mutex_destroy(&local_mtx);
}
void
bad1(void)
{
@ -486,3 +507,9 @@ void bad32(void) {
lck_rw_lock_exclusive(&rw);
lck_rw_unlock_shared(&rw); // FIXME: warn - should be exclusive?
}
void bad33(void) {
pthread_mutex_lock(pmtx);
fake_system_function();
pthread_mutex_lock(pmtx); // expected-warning{{This lock has already been acquired}}
}