Skip to content

Commit 08de00a

Browse files
authored
Thread Safety Analysis: Fix recursive capability alias resolution (#159921)
Fix a false positive in thread safety alias analysis caused by incorrect late resolution of aliases. The analysis previously failed to distinguish between an alias and its defining expression; reassigning a variable within that expression (e.g., `ptr` in `alias = ptr->field`) would incorrectly change the dependent alias as well. The fix is to properly use LocalVariableMap::lookupExpr's updated context in a recursive lookup. Reported-by: Christoph Hellwig <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 242a1e2 commit 08de00a

File tree

4 files changed

+57
-24
lines changed

4 files changed

+57
-24
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,10 +543,14 @@ class SExprBuilder {
543543
til::BasicBlock *CurrentBB = nullptr;
544544
BlockInfo *CurrentBlockInfo = nullptr;
545545

546+
// The closure that captures state required for the lookup; this may be
547+
// mutable, so we have to save/restore before/after recursive lookups.
548+
using LookupLocalVarExprClosure =
549+
std::function<const Expr *(const NamedDecl *)>;
546550
// Recursion guard.
547551
llvm::DenseSet<const ValueDecl *> VarsBeingTranslated;
548552
// Context-dependent lookup of currently valid definitions of local variables.
549-
std::function<const Expr *(const NamedDecl *)> LookupLocalVarExpr;
553+
LookupLocalVarExprClosure LookupLocalVarExpr;
550554
};
551555

552556
#ifndef NDEBUG

clang/lib/Analysis/ThreadSafety.cpp

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,13 +1668,13 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
16681668
const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];
16691669
const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
16701670

1671-
// Temporarily set the lookup context for SExprBuilder.
1672-
SxBuilder.setLookupLocalVarExpr([&](const NamedDecl *D) -> const Expr * {
1673-
if (!Handler.issueBetaWarnings())
1674-
return nullptr;
1675-
auto Ctx = LVarCtx;
1676-
return LocalVarMap.lookupExpr(D, Ctx);
1677-
});
1671+
if (Handler.issueBetaWarnings()) {
1672+
// Temporarily set the lookup context for SExprBuilder.
1673+
SxBuilder.setLookupLocalVarExpr(
1674+
[this, Ctx = LVarCtx](const NamedDecl *D) mutable -> const Expr * {
1675+
return LocalVarMap.lookupExpr(D, Ctx);
1676+
});
1677+
}
16781678
auto Cleanup = llvm::make_scope_exit(
16791679
[this] { SxBuilder.setLookupLocalVarExpr(nullptr); });
16801680

@@ -1722,6 +1722,19 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
17221722
LocalVariableMap::Context LVarCtx;
17231723
unsigned CtxIndex;
17241724

1725+
// To update and adjust the context.
1726+
void updateLocalVarMapCtx(const Stmt *S) {
1727+
if (S)
1728+
LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
1729+
if (!Analyzer->Handler.issueBetaWarnings())
1730+
return;
1731+
// The lookup closure needs to be reconstructed with the refreshed LVarCtx.
1732+
Analyzer->SxBuilder.setLookupLocalVarExpr(
1733+
[this, Ctx = LVarCtx](const NamedDecl *D) mutable -> const Expr * {
1734+
return Analyzer->LocalVarMap.lookupExpr(D, Ctx);
1735+
});
1736+
}
1737+
17251738
// helper functions
17261739

17271740
void checkAccess(const Expr *Exp, AccessKind AK,
@@ -1747,13 +1760,7 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
17471760
: ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
17481761
FunctionExitFSet(FunctionExitFSet), LVarCtx(Info.EntryContext),
17491762
CtxIndex(Info.EntryIndex) {
1750-
Analyzer->SxBuilder.setLookupLocalVarExpr(
1751-
[this](const NamedDecl *D) -> const Expr * {
1752-
if (!Analyzer->Handler.issueBetaWarnings())
1753-
return nullptr;
1754-
auto Ctx = LVarCtx;
1755-
return Analyzer->LocalVarMap.lookupExpr(D, Ctx);
1756-
});
1763+
updateLocalVarMapCtx(nullptr);
17571764
}
17581765

17591766
~BuildLockset() { Analyzer->SxBuilder.setLookupLocalVarExpr(nullptr); }
@@ -2259,9 +2266,7 @@ void BuildLockset::VisitBinaryOperator(const BinaryOperator *BO) {
22592266
if (!BO->isAssignmentOp())
22602267
return;
22612268

2262-
// adjust the context
2263-
LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
2264-
2269+
updateLocalVarMapCtx(BO);
22652270
checkAccess(BO->getLHS(), AK_Written);
22662271
}
22672272

@@ -2307,8 +2312,7 @@ void BuildLockset::examineArguments(const FunctionDecl *FD,
23072312
}
23082313

23092314
void BuildLockset::VisitCallExpr(const CallExpr *Exp) {
2310-
// adjust the context
2311-
LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, Exp, LVarCtx);
2315+
updateLocalVarMapCtx(Exp);
23122316

23132317
if (const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
23142318
const auto *ME = dyn_cast<MemberExpr>(CE->getCallee());
@@ -2404,8 +2408,7 @@ static const Expr *UnpackConstruction(const Expr *E) {
24042408
}
24052409

24062410
void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
2407-
// adjust the context
2408-
LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
2411+
updateLocalVarMapCtx(S);
24092412

24102413
for (auto *D : S->getDeclGroup()) {
24112414
if (auto *VD = dyn_cast_or_null<VarDecl>(D)) {

clang/lib/Analysis/ThreadSafetyCommon.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,17 @@ til::SExpr *SExprBuilder::translateVariable(const VarDecl *VD,
248248
// defining VD, use its pre-assignment value to break the cycle.
249249
if (VarsBeingTranslated.contains(VD->getCanonicalDecl()))
250250
return new (Arena) til::LiteralPtr(VD);
251-
VarsBeingTranslated.insert(VD->getCanonicalDecl());
251+
252+
// The closure captures state that is updated to correctly translate chains of
253+
// aliases. Restore it when we are done with recursive translation.
252254
auto Cleanup = llvm::make_scope_exit(
253-
[&] { VarsBeingTranslated.erase(VD->getCanonicalDecl()); });
255+
[&, RestoreClosure =
256+
VarsBeingTranslated.empty() ? LookupLocalVarExpr : nullptr] {
257+
VarsBeingTranslated.erase(VD->getCanonicalDecl());
258+
if (VarsBeingTranslated.empty())
259+
LookupLocalVarExpr = RestoreClosure;
260+
});
261+
VarsBeingTranslated.insert(VD->getCanonicalDecl());
254262

255263
QualType Ty = VD->getType();
256264
if (!VD->isStaticLocal() && Ty->isPointerType()) {

clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7463,6 +7463,7 @@ void testNestedAcquire(Container *c) EXCLUSIVE_LOCK_FUNCTION(&c->foo.mu) {
74637463

74647464
struct ContainerOfPtr {
74657465
Foo *foo_ptr;
7466+
ContainerOfPtr *next;
74667467
};
74677468

74687469
void testIndirectAccess(ContainerOfPtr *fc) {
@@ -7472,6 +7473,23 @@ void testIndirectAccess(ContainerOfPtr *fc) {
74727473
ptr->mu.Unlock();
74737474
}
74747475

7476+
void testAliasChainUnrelatedReassignment1(ContainerOfPtr *list) {
7477+
Foo *eb = list->foo_ptr;
7478+
eb->mu.Lock();
7479+
list = list->next;
7480+
eb->data = 42;
7481+
eb->mu.Unlock();
7482+
}
7483+
7484+
void testAliasChainUnrelatedReassignment2(ContainerOfPtr *list) {
7485+
ContainerOfPtr *busyp = list;
7486+
Foo *eb = busyp->foo_ptr;
7487+
eb->mu.Lock();
7488+
busyp = busyp->next;
7489+
eb->data = 42;
7490+
eb->mu.Unlock();
7491+
}
7492+
74757493
void testControlFlowDoWhile(Foo *f, int x) {
74767494
Foo *ptr = f;
74777495

0 commit comments

Comments
 (0)