[clang] [analyzer] Fix null-pointer dereference in PthreadLockChecker (PR #210912)
Arseniy Zaostrovnykh via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 21 07:05:40 PDT 2026
https://github.com/necto updated https://github.com/llvm/llvm-project/pull/210912
>From 9d7ad66b8e999fbc3b80014d47b122dad54a749f Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto.ne at gmail.com>
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 56e4eda823080ae4331bf71df8a1bf59afb800bf Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto.ne at gmail.com>
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());
More information about the cfe-commits
mailing list