Skip to content

Commit cf8e189

Browse files
committed
[clang][TSA] Thread safety cleanup functions
Consider cleanup functions in thread safety analysis. Differential Revision: https://reviews.llvm.org/D152504
1 parent 5f476b8 commit cf8e189

File tree

4 files changed

+36
-6
lines changed

4 files changed

+36
-6
lines changed

clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ class SExprBuilder {
361361
unsigned NumArgs = 0;
362362

363363
// Function arguments
364-
const Expr *const *FunArgs = nullptr;
364+
llvm::PointerUnion<const Expr *const *, til::SExpr *> FunArgs = nullptr;
365365

366366
// is Self referred to with -> or .?
367367
bool SelfArrow = false;

clang/lib/Analysis/ThreadSafety.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1773,7 +1773,8 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK,
17731773
///
17741774
/// \param Exp The call expression.
17751775
/// \param D The callee declaration.
1776-
/// \param Self If \p Exp = nullptr, the implicit this argument.
1776+
/// \param Self If \p Exp = nullptr, the implicit this argument or the argument
1777+
/// of an implicitly called cleanup function.
17771778
/// \param Loc If \p Exp = nullptr, the location.
17781779
void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
17791780
til::LiteralPtr *Self, SourceLocation Loc) {
@@ -2417,6 +2418,15 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
24172418
AD.getTriggerStmt()->getEndLoc());
24182419
break;
24192420
}
2421+
2422+
case CFGElement::CleanupFunction: {
2423+
const CFGCleanupFunction &CF = BI.castAs<CFGCleanupFunction>();
2424+
LocksetBuilder.handleCall(/*Exp=*/nullptr, CF.getFunctionDecl(),
2425+
SxBuilder.createVariable(CF.getVarDecl()),
2426+
CF.getVarDecl()->getLocation());
2427+
break;
2428+
}
2429+
24202430
case CFGElement::TemporaryDtor: {
24212431
auto TD = BI.castAs<CFGTemporaryDtor>();
24222432

clang/lib/Analysis/ThreadSafetyCommon.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ static StringRef ClassifyDiagnostic(QualType VDT) {
110110
/// \param D The declaration to which the attribute is attached.
111111
/// \param DeclExp An expression involving the Decl to which the attribute
112112
/// is attached. E.g. the call to a function.
113-
/// \param Self S-expression to substitute for a \ref CXXThisExpr.
113+
/// \param Self S-expression to substitute for a \ref CXXThisExpr in a call,
114+
/// or argument to a cleanup function.
114115
CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
115116
const NamedDecl *D,
116117
const Expr *DeclExp,
@@ -144,7 +145,11 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
144145

145146
if (Self) {
146147
assert(!Ctx.SelfArg && "Ambiguous self argument");
147-
Ctx.SelfArg = Self;
148+
assert(isa<FunctionDecl>(D) && "Self argument requires function");
149+
if (isa<CXXMethodDecl>(D))
150+
Ctx.SelfArg = Self;
151+
else
152+
Ctx.FunArgs = Self;
148153

149154
// If the attribute has no arguments, then assume the argument is "this".
150155
if (!AttrExp)
@@ -312,8 +317,14 @@ til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE,
312317
? (cast<FunctionDecl>(D)->getCanonicalDecl() == Canonical)
313318
: (cast<ObjCMethodDecl>(D)->getCanonicalDecl() == Canonical)) {
314319
// Substitute call arguments for references to function parameters
315-
assert(I < Ctx->NumArgs);
316-
return translate(Ctx->FunArgs[I], Ctx->Prev);
320+
if (const Expr *const *FunArgs =
321+
Ctx->FunArgs.dyn_cast<const Expr *const *>()) {
322+
assert(I < Ctx->NumArgs);
323+
return translate(FunArgs[I], Ctx->Prev);
324+
}
325+
326+
assert(I == 0);
327+
return Ctx->FunArgs.get<til::SExpr *>();
317328
}
318329
}
319330
// Map the param back to the param of the original function declaration

clang/test/Sema/warn-thread-safety-analysis.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ int get_value(int *p) SHARED_LOCKS_REQUIRED(foo_.mu_){
7272
return *p;
7373
}
7474

75+
void unlock_scope(struct Mutex *const *mu) __attribute__((release_capability(**mu)));
76+
7577
int main(void) {
7678

7779
Foo_fun1(1); // expected-warning{{calling function 'Foo_fun1' requires holding mutex 'mu2'}} \
@@ -127,6 +129,13 @@ int main(void) {
127129
// expected-note@-1{{mutex released here}}
128130
mutex_shared_unlock(&mu1); // expected-warning {{releasing mutex 'mu1' that was not held}}
129131

132+
/// Cleanup functions
133+
{
134+
struct Mutex* const __attribute__((cleanup(unlock_scope))) scope = &mu1;
135+
mutex_exclusive_lock(scope); // Note that we have to lock through scope, because no alias analysis!
136+
// Cleanup happens automatically -> no warning.
137+
}
138+
130139
return 0;
131140
}
132141

0 commit comments

Comments
 (0)