https://github.com/necto created https://github.com/llvm/llvm-project/pull/210912
PthreadLockChecker assumes `pthread_mutex_lock` is always called in a feasible state, which is not true. In particular, in ZFS the analyzer crashes when runs in CTU mode because it reaches `pthread_mutex_lock()` in over-constraint state (see the reduced example in the first commit). -- CPP-8665 >From e942ab2cf50d216b7a1fcb26ab3b977863831d82 Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 20 Jul 2026 13:20:00 +0200 Subject: [PATCH 1/2] Crashing test case --- clang/test/Analysis/pthreadlock.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/clang/test/Analysis/pthreadlock.c b/clang/test/Analysis/pthreadlock.c index e931569c45ab8..6320a3d34600b 100644 --- a/clang/test/Analysis/pthreadlock.c +++ b/clang/test/Analysis/pthreadlock.c @@ -18,6 +18,25 @@ lck_rw_t rw; #define NULL 0 +long global_var; +void noCrash(void) { + // Produce a complicated self-contradictory constraint + if ((global_var >> 48 & (1ULL << 8) - 1) == 2) { + } + if (global_var >> 48 & (1ULL << 8) - 1 & 8) { + } + pthread_mutex_lock(&mtx1); // no-warning +} + +void noCrashTryLock(void) { + // Produce a complicated self-contradictory constraint + if ((global_var >> 48 & (1ULL << 8) - 1) == 2) { + } + if (global_var >> 48 & (1ULL << 8) - 1 & 8) { + } + pthread_mutex_trylock(&mtx1); // no-warning +} + void ok1(void) { >From c2d2bfca345812e37b9900e62c532d68d45d08c0 Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Tue, 21 Jul 2026 10:39:09 +0200 Subject: [PATCH 2/2] Fix PthreadLockChecker --- .../Checkers/PthreadLockChecker.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index 6a1c7a93fb773..c3540ddc32e3a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -493,8 +493,11 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, default: llvm_unreachable("Unknown tryLock locking semantics"); } - assert(lockFail && lockSucc); - C.addTransition(lockFail); + // The state where the lock failed can be infeasible if the constraint + // solver only now discovers a contradiction in the accumulated + // constraints; only take that transition when it is feasible. + if (lockFail) + C.addTransition(lockFail); } // We might want to handle the case when the mutex lock function was inlined // and returned an Unknown or Undefined value. @@ -505,7 +508,9 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, // FIXME: If the lock function was inlined and returned true, // we need to behave sanely - at least generate sink. lockSucc = state->assume(*DefinedRetVal, false); - assert(lockSucc); + // `lockSucc` can be null here if the constraint solver only now detects + // a contradiction in the accumulated constraints; the shared guard below + // prunes this infeasible path. } // We might want to handle the case when the mutex lock function was inlined // and returned an Unknown or Undefined value. @@ -515,6 +520,12 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, lockSucc = state; } + // If the constraint solver determined the lock-acquired path is infeasible + // (which can surface here when it only now detects a contradiction in the + // accumulated constraints), prune this path. + if (!lockSucc) + return; + // Record that the lock was acquired. lockSucc = lockSucc->add<LockSet>(lockR); lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
