-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[clang][analyzer] Add defer_lock_t modelling to BlockInCriticalSectionChecker #168338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@llvm/pr-subscribers-clang-static-analyzer-1 Author: Endre Fülöp (gamesh411) ChangesFixes #166573 Full diff: https://github.com/llvm/llvm-project/pull/168338.diff 2 Files Affected:
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 68bee710e5ce5..99137e038811d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -115,7 +115,25 @@ class RAIIMutexDescriptor {
return false;
const IdentifierInfo *II =
cast<CXXRecordDecl>(C->getDecl()->getParent())->getIdentifier();
- return II == Guard;
+ if (II != Guard)
+ return false;
+
+ // For unique_lock, check if it's constructed with a ctor that takes the tag
+ // type defer_lock_t. In this case, the lock is not acquired.
+ if constexpr (std::is_same_v<T, CXXConstructorCall>) {
+ if (GuardName == "unique_lock" && C->getNumArgs() >= 2) {
+ const Expr *SecondArg = C->getArgExpr(1);
+ QualType ArgType = SecondArg->getType().getNonReferenceType();
+ QualType UnqualifiedType = ArgType.getUnqualifiedType();
+ if (const auto *RD = UnqualifiedType->getAsRecordDecl();
+ RD && RD->getName() == "defer_lock_t" &&
+ RD->getDeclContext()->isStdNamespace()) {
+ return false;
+ }
+ }
+ }
+
+ return true;
}
public:
diff --git a/clang/test/Analysis/block-in-critical-section.cpp b/clang/test/Analysis/block-in-critical-section.cpp
index ee9a708f231a8..674a09265faa5 100644
--- a/clang/test/Analysis/block-in-critical-section.cpp
+++ b/clang/test/Analysis/block-in-critical-section.cpp
@@ -16,9 +16,12 @@ 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>
@@ -309,3 +312,9 @@ void testTrylockCurrentlyFalsePositive(pthread_mutex_t *m) {
// 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
+}
|
|
@llvm/pr-subscribers-clang Author: Endre Fülöp (gamesh411) ChangesFixes #166573 Full diff: https://github.com/llvm/llvm-project/pull/168338.diff 2 Files Affected:
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 68bee710e5ce5..99137e038811d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -115,7 +115,25 @@ class RAIIMutexDescriptor {
return false;
const IdentifierInfo *II =
cast<CXXRecordDecl>(C->getDecl()->getParent())->getIdentifier();
- return II == Guard;
+ if (II != Guard)
+ return false;
+
+ // For unique_lock, check if it's constructed with a ctor that takes the tag
+ // type defer_lock_t. In this case, the lock is not acquired.
+ if constexpr (std::is_same_v<T, CXXConstructorCall>) {
+ if (GuardName == "unique_lock" && C->getNumArgs() >= 2) {
+ const Expr *SecondArg = C->getArgExpr(1);
+ QualType ArgType = SecondArg->getType().getNonReferenceType();
+ QualType UnqualifiedType = ArgType.getUnqualifiedType();
+ if (const auto *RD = UnqualifiedType->getAsRecordDecl();
+ RD && RD->getName() == "defer_lock_t" &&
+ RD->getDeclContext()->isStdNamespace()) {
+ return false;
+ }
+ }
+ }
+
+ return true;
}
public:
diff --git a/clang/test/Analysis/block-in-critical-section.cpp b/clang/test/Analysis/block-in-critical-section.cpp
index ee9a708f231a8..674a09265faa5 100644
--- a/clang/test/Analysis/block-in-critical-section.cpp
+++ b/clang/test/Analysis/block-in-critical-section.cpp
@@ -16,9 +16,12 @@ 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>
@@ -309,3 +312,9 @@ void testTrylockCurrentlyFalsePositive(pthread_mutex_t *m) {
// 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
+}
|
NagyDonat
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with a few minor suggestions.
clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
Outdated
Show resolved
Hide resolved
clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
Outdated
Show resolved
Hide resolved
steakhal
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review. LGTM for me, the raised suggestions look good to me.
…r.cpp Co-authored-by: Donát Nagy <[email protected]>
…r.cpp Co-authored-by: Donát Nagy <[email protected]>
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
|
@NagyDonat I may have overcomplicated that part. Thanks for the suggestions. I have applied them. |
🐧 Linux x64 Test Results
|
Fixes #166573