aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Analysis/ThreadSafety.cpp
diff options
context:
space:
mode:
authorDan McArdle <zingermc@gmail.com>2024-06-24 09:29:13 -0400
committerGitHub <noreply@github.com>2024-06-24 09:29:13 -0400
commitc1bde0a2cb640b3607e9568b9a57b292e1f82666 (patch)
tree9fff28900c6e0fb1b94877418d69cb006bd0e6ac /clang/lib/Analysis/ThreadSafety.cpp
parent5cd0ba30f53d11835dbfd05ad4071d397387fb04 (diff)
downloadllvm-c1bde0a2cb640b3607e9568b9a57b292e1f82666.zip
llvm-c1bde0a2cb640b3607e9568b9a57b292e1f82666.tar.gz
llvm-c1bde0a2cb640b3607e9568b9a57b292e1f82666.tar.bz2
[clang][ThreadSafety] Check trylock function success and return types (#95290)
With this change, Clang will generate errors when trylock functions have improper return types. Today, it silently fails to apply the trylock attribute to these functions which may incorrectly lead users to believe they have correctly acquired locks before accessing guarded data. As a side effect of explicitly checking the success argument type, I seem to have fixed a false negative in the analysis that could occur when a trylock's success argument is an enumerator. I've added a regression test to warn-thread-safety-analysis.cpp named `TrylockSuccessEnumFalseNegative`. This change also improves the documentation with descriptions of of the subtle gotchas that arise from the analysis interpreting the success arg as a boolean. Issue #92408
Diffstat (limited to 'clang/lib/Analysis/ThreadSafety.cpp')
-rw-r--r--clang/lib/Analysis/ThreadSafety.cpp82
1 files changed, 46 insertions, 36 deletions
diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp
index e25b843..550c2a3 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -37,6 +37,7 @@
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ImmutableMap.h"
@@ -1034,9 +1035,10 @@ public:
template <class AttrType>
void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp,
- const NamedDecl *D,
- const CFGBlock *PredBlock, const CFGBlock *CurrBlock,
- Expr *BrE, bool Neg);
+ const NamedDecl *D, const CFGBlock *PredBlock,
+ const CFGBlock *CurrBlock,
+ const ASTContext &TrylockAttrContext,
+ Expr *TrylockAttrSuccessValue, bool Neg);
const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C,
bool &Negate);
@@ -1359,17 +1361,18 @@ void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
const Expr *Exp, const NamedDecl *D,
const CFGBlock *PredBlock,
const CFGBlock *CurrBlock,
- Expr *BrE, bool Neg) {
- // Find out which branch has the lock
- bool branch = false;
- if (const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
- branch = BLE->getValue();
- else if (const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
- branch = ILE->getValue().getBoolValue();
-
- int branchnum = branch ? 0 : 1;
- if (Neg)
- branchnum = !branchnum;
+ const ASTContext &TrylockAttrContext,
+ Expr *TrylockAttrSuccess,
+ bool Neg) {
+ // Evaluate the trylock's success value as a boolean.
+ bool trylockSuccessValue = false;
+ if (!TrylockAttrSuccess->EvaluateAsBooleanCondition(
+ trylockSuccessValue, TrylockAttrContext,
+ /*InConstantContext=*/true)) {
+ llvm_unreachable("Trylock success value could not be evaluated.");
+ }
+
+ const int branchnum = Neg ? !!trylockSuccessValue : !trylockSuccessValue;
// If we've taken the trylock branch, then add the lock
int i = 0;
@@ -1390,8 +1393,15 @@ static bool getStaticBooleanValue(Expr *E, bool &TCond) {
} else if (const auto *ILE = dyn_cast<IntegerLiteral>(E)) {
TCond = ILE->getValue().getBoolValue();
return true;
- } else if (auto *CE = dyn_cast<ImplicitCastExpr>(E))
+ } else if (auto *CE = dyn_cast<ImplicitCastExpr>(E)) {
return getStaticBooleanValue(CE->getSubExpr(), TCond);
+ } else if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
+ llvm::APSInt IV = ECD->getInitVal();
+ TCond = IV.getBoolValue();
+ return true;
+ }
+ }
return false;
}
@@ -1497,27 +1507,27 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
// If the condition is a call to a Trylock function, then grab the attributes
for (const auto *Attr : FunDecl->attrs()) {
switch (Attr->getKind()) {
- case attr::TryAcquireCapability: {
- auto *A = cast<TryAcquireCapabilityAttr>(Attr);
- getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
- Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),
- Negate);
- break;
- };
- case attr::ExclusiveTrylockFunction: {
- const auto *A = cast<ExclusiveTrylockFunctionAttr>(Attr);
- getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
- A->getSuccessValue(), Negate);
- break;
- }
- case attr::SharedTrylockFunction: {
- const auto *A = cast<SharedTrylockFunctionAttr>(Attr);
- getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
- A->getSuccessValue(), Negate);
- break;
- }
- default:
- break;
+ case attr::TryAcquireCapability: {
+ auto *A = cast<TryAcquireCapabilityAttr>(Attr);
+ getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
+ Exp, FunDecl, PredBlock, CurrBlock, FunDecl->getASTContext(),
+ A->getSuccessValue(), Negate);
+ break;
+ };
+ case attr::ExclusiveTrylockFunction: {
+ const auto *A = cast<ExclusiveTrylockFunctionAttr>(Attr);
+ getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
+ FunDecl->getASTContext(), A->getSuccessValue(), Negate);
+ break;
+ }
+ case attr::SharedTrylockFunction: {
+ const auto *A = cast<SharedTrylockFunctionAttr>(Attr);
+ getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
+ FunDecl->getASTContext(), A->getSuccessValue(), Negate);
+ break;
+ }
+ default:
+ break;
}
}