Skip to content

Commit 0348e71

Browse files
authored
[clang][dataflow] Fix crash when operator= result type is not destination type. (#90898)
The existing code was full of comments about how we assume this is always the case, but it's not mandated by the standard, and there is code out there that returns a different type. So check that the result type is in fact the same as the destination type before attempting to copy to the result. To make sure that we don't bail out in more cases than intended, I've extended existing tests to verify that in the common case, we do return the destination object (by reference or value, as the case may be).
1 parent d70267f commit 0348e71

File tree

2 files changed

+86
-8
lines changed

2 files changed

+86
-8
lines changed

clang/lib/Analysis/FlowSensitive/Transfer.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -556,14 +556,23 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
556556

557557
copyRecord(*LocSrc, *LocDst, Env);
558558

559-
// If the expr is a glvalue, we can reasonably assume the operator is
560-
// returning T& and thus we can assign it `LocDst`.
561-
if (S->isGLValue()) {
559+
// The assignment operator can have an arbitrary return type. We model the
560+
// return value only if the return type is the same as or a base class of
561+
// the destination type.
562+
if (S->getType().getCanonicalType().getUnqualifiedType() !=
563+
LocDst->getType().getCanonicalType().getUnqualifiedType()) {
564+
auto ReturnDecl = S->getType()->getAsCXXRecordDecl();
565+
auto DstDecl = LocDst->getType()->getAsCXXRecordDecl();
566+
if (ReturnDecl == nullptr || DstDecl == nullptr)
567+
return;
568+
if (!DstDecl->isDerivedFrom(ReturnDecl))
569+
return;
570+
}
571+
572+
if (S->isGLValue())
562573
Env.setStorageLocation(*S, *LocDst);
563-
} else if (S->getType()->isRecordType()) {
564-
// Assume that the assignment returns the assigned value.
574+
else
565575
copyRecord(*LocDst, Env.getResultObjectLocation(*S), Env);
566-
}
567576

568577
return;
569578
}

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2228,7 +2228,7 @@ TEST(TransferTest, AssignmentOperator) {
22282228
A Foo = { 1 };
22292229
A Bar = { 2 };
22302230
// [[p1]]
2231-
Foo = Bar;
2231+
A &Rval = (Foo = Bar);
22322232
// [[p2]]
22332233
Foo.Baz = 3;
22342234
// [[p3]]
@@ -2274,6 +2274,7 @@ TEST(TransferTest, AssignmentOperator) {
22742274
cast<RecordStorageLocation>(Env2.getStorageLocation(*BarDecl));
22752275

22762276
EXPECT_TRUE(recordsEqual(*FooLoc2, *BarLoc2, Env2));
2277+
EXPECT_EQ(&getLocForDecl(ASTCtx, Env2, "Rval"), FooLoc2);
22772278

22782279
const auto *FooBazVal2 =
22792280
cast<IntegerValue>(getFieldValue(FooLoc2, *BazDecl, Env2));
@@ -2441,7 +2442,75 @@ TEST(TransferTest, AssignmentOperatorReturnsByValue) {
24412442
// This is a crash repro.
24422443
std::string Code = R"(
24432444
struct S {
2444-
S operator=(S&& other);
2445+
S operator=(const S&);
2446+
int i;
2447+
};
2448+
void target() {
2449+
S S1 = { 1 };
2450+
S S2 = { 2 };
2451+
S S3 = { 3 };
2452+
// [[before]]
2453+
// Test that the returned value is modeled by assigning to another value.
2454+
S1 = (S2 = S3);
2455+
(void)0;
2456+
// [[after]]
2457+
}
2458+
)";
2459+
runDataflow(
2460+
Code,
2461+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2462+
ASTContext &ASTCtx) {
2463+
const ValueDecl *S1Decl = findValueDecl(ASTCtx, "S1");
2464+
const ValueDecl *S2Decl = findValueDecl(ASTCtx, "S2");
2465+
const ValueDecl *S3Decl = findValueDecl(ASTCtx, "S3");
2466+
2467+
const Environment &EnvBefore =
2468+
getEnvironmentAtAnnotation(Results, "before");
2469+
2470+
EXPECT_FALSE(recordsEqual(
2471+
*EnvBefore.get<RecordStorageLocation>(*S1Decl),
2472+
*EnvBefore.get<RecordStorageLocation>(*S2Decl), EnvBefore));
2473+
EXPECT_FALSE(recordsEqual(
2474+
*EnvBefore.get<RecordStorageLocation>(*S2Decl),
2475+
*EnvBefore.get<RecordStorageLocation>(*S3Decl), EnvBefore));
2476+
2477+
const Environment &EnvAfter =
2478+
getEnvironmentAtAnnotation(Results, "after");
2479+
2480+
EXPECT_TRUE(recordsEqual(*EnvAfter.get<RecordStorageLocation>(*S1Decl),
2481+
*EnvAfter.get<RecordStorageLocation>(*S2Decl),
2482+
EnvAfter));
2483+
EXPECT_TRUE(recordsEqual(*EnvAfter.get<RecordStorageLocation>(*S2Decl),
2484+
*EnvAfter.get<RecordStorageLocation>(*S3Decl),
2485+
EnvAfter));
2486+
});
2487+
}
2488+
2489+
TEST(TransferTest, AssignmentOperatorReturnsDifferentTypeByRef) {
2490+
// This is a crash repro.
2491+
std::string Code = R"(
2492+
struct DifferentType {};
2493+
struct S {
2494+
DifferentType& operator=(const S&);
2495+
};
2496+
void target() {
2497+
S s;
2498+
s = S();
2499+
// [[p]]
2500+
}
2501+
)";
2502+
runDataflow(
2503+
Code,
2504+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2505+
ASTContext &ASTCtx) {});
2506+
}
2507+
2508+
TEST(TransferTest, AssignmentOperatorReturnsDifferentTypeByValue) {
2509+
// This is a crash repro.
2510+
std::string Code = R"(
2511+
struct DifferentType {};
2512+
struct S {
2513+
DifferentType operator=(const S&);
24452514
};
24462515
void target() {
24472516
S s;

0 commit comments

Comments
 (0)