Skip to content

Commit 7af97ae

Browse files
committed
[clang] Fix crash in dynamic_cast final class optimization
This corrects the codegen for the final class optimization to correct handle the case where there is no path to perform the cast, and also corrects the codegen to handle ptrauth protected vtable pointers. As part of this fix we separate out the path computation as that makes it easier to reason about the failure code paths and more importantly means we can know what the type of the this object is during the cast. The allows us to use the GetVTablePointer interface which correctly performs the authentication operations required when pointer authentication is enabled. There is one place where we still lose a fully authenticated path, and that is if there multiple paths from the source type to the destination type. In that case we're forced to perform a dynamic_cast to void* to find the primary base. As we do not know the primary base at this point we do not yet know the dynamic type of the adjusted this object and so cannot authenticate the vtable load. The approach this PR takes to mitigate this gap is to authenticate the vtable of the original object, and then if the stripped vtable pointer matches the expected type we then know the type of the object and so perform a fully authenticated load of the vtable from the resulting object. Fixes #137518
1 parent 9f7f3d6 commit 7af97ae

File tree

6 files changed

+298
-35
lines changed

6 files changed

+298
-35
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ Bug Fixes to C++ Support
160160
- Fix a crash when deleting a pointer to an incomplete array (#GH150359).
161161
- Fix an assertion failure when expression in assumption attribute
162162
(``[[assume(expr)]]``) creates temporary objects.
163+
- Fix the dynamic_cast to final class optimization to correctly handle
164+
casts that are guaranteed to fail, and correctly handle ptrauth protected
165+
vtable pointers (#GH137518).
163166

164167
Bug Fixes to AST Handling
165168
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/CodeGen/CGExprCXX.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,11 +2344,14 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(Address ThisAddr,
23442344
EmitBlock(CastEnd);
23452345

23462346
if (CastNull) {
2347-
llvm::PHINode *PHI = Builder.CreatePHI(Value->getType(), 2);
2348-
PHI->addIncoming(Value, CastNotNull);
2349-
PHI->addIncoming(NullValue, CastNull);
2350-
2351-
Value = PHI;
2347+
if (CastNotNull) {
2348+
llvm::PHINode *PHI = Builder.CreatePHI(Value->getType(), 2);
2349+
PHI->addIncoming(Value, CastNotNull);
2350+
PHI->addIncoming(NullValue, CastNull);
2351+
2352+
Value = PHI;
2353+
} else
2354+
Value = NullValue;
23522355
}
23532356

23542357
return Value;

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,15 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
226226
return hasUniqueVTablePointer(DestRecordTy);
227227
}
228228

229+
struct ExactDynamicCastInfo {
230+
bool RequiresCastToPrimaryBase;
231+
std::optional<CharUnits> Offset;
232+
};
233+
234+
ExactDynamicCastInfo getExactDynamicCastInfo(QualType SrcRecordTy,
235+
QualType DestTy,
236+
QualType DestRecordTy);
237+
229238
llvm::Value *emitDynamicCastCall(CodeGenFunction &CGF, Address Value,
230239
QualType SrcRecordTy, QualType DestTy,
231240
QualType DestRecordTy,
@@ -1681,10 +1690,11 @@ llvm::Value *ItaniumCXXABI::emitDynamicCastCall(
16811690
return Value;
16821691
}
16831692

1684-
llvm::Value *ItaniumCXXABI::emitExactDynamicCast(
1685-
CodeGenFunction &CGF, Address ThisAddr, QualType SrcRecordTy,
1686-
QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastSuccess,
1687-
llvm::BasicBlock *CastFail) {
1693+
ItaniumCXXABI::ExactDynamicCastInfo
1694+
ItaniumCXXABI::getExactDynamicCastInfo(QualType SrcRecordTy, QualType DestTy,
1695+
QualType DestRecordTy) {
1696+
assert(shouldEmitExactDynamicCast(DestRecordTy));
1697+
16881698
ASTContext &Context = getContext();
16891699

16901700
// Find all the inheritance paths.
@@ -1722,41 +1732,102 @@ llvm::Value *ItaniumCXXABI::emitExactDynamicCast(
17221732
if (!Offset)
17231733
Offset = PathOffset;
17241734
else if (Offset != PathOffset) {
1725-
// Base appears in at least two different places. Find the most-derived
1726-
// object and see if it's a DestDecl. Note that the most-derived object
1727-
// must be at least as aligned as this base class subobject, and must
1728-
// have a vptr at offset 0.
1729-
ThisAddr = Address(emitDynamicCastToVoid(CGF, ThisAddr, SrcRecordTy),
1730-
CGF.VoidPtrTy, ThisAddr.getAlignment());
1731-
SrcDecl = DestDecl;
1732-
Offset = CharUnits::Zero();
1733-
break;
1735+
// Base appears in at least two different places.
1736+
return ExactDynamicCastInfo{/*RequiresCastToPrimaryBase=*/true,
1737+
CharUnits::Zero()};
17341738
}
17351739
}
17361740

1737-
if (!Offset) {
1741+
return ExactDynamicCastInfo{/*RequiresCastToPrimaryBase=*/false, Offset};
1742+
}
1743+
1744+
llvm::Value *ItaniumCXXABI::emitExactDynamicCast(
1745+
CodeGenFunction &CGF, Address ThisAddr, QualType SrcRecordTy,
1746+
QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastSuccess,
1747+
llvm::BasicBlock *CastFail) {
1748+
const CXXRecordDecl *SrcDecl = SrcRecordTy->getAsCXXRecordDecl();
1749+
const CXXRecordDecl *DestDecl = DestRecordTy->getAsCXXRecordDecl();
1750+
auto AuthenticateVTable = [&](Address ThisAddr, const CXXRecordDecl *Decl) {
1751+
if (!CGF.getLangOpts().PointerAuthCalls)
1752+
return;
1753+
(void)CGF.GetVTablePtr(ThisAddr, CGF.UnqualPtrTy, Decl,
1754+
CodeGenFunction::VTableAuthMode::MustTrap);
1755+
};
1756+
1757+
ExactDynamicCastInfo ExactCastInfo =
1758+
getExactDynamicCastInfo(SrcRecordTy, DestTy, DestRecordTy);
1759+
if (!ExactCastInfo.Offset) {
17381760
// If there are no public inheritance paths, the cast always fails.
1761+
AuthenticateVTable(ThisAddr, SrcDecl);
17391762
CGF.EmitBranch(CastFail);
17401763
return llvm::PoisonValue::get(CGF.VoidPtrTy);
17411764
}
17421765

1766+
bool PerformPostCastAuthentication = false;
1767+
llvm::Value *VTable = nullptr;
1768+
if (ExactCastInfo.RequiresCastToPrimaryBase) {
1769+
// Base appears in at least two different places. Find the most-derived
1770+
// object and see if it's a DestDecl. Note that the most-derived object
1771+
// must be at least as aligned as this base class subobject, and must
1772+
// have a vptr at offset 0.
1773+
llvm::Value *PrimaryBase =
1774+
emitDynamicCastToVoid(CGF, ThisAddr, SrcRecordTy);
1775+
ThisAddr = Address(PrimaryBase, CGF.VoidPtrTy, ThisAddr.getAlignment());
1776+
SrcDecl = DestDecl;
1777+
// This unauthenticated load is unavoidable, so we're relying on the
1778+
// authenticated load in the dynamic cast to void, and we'll manually
1779+
// authenticate the resulting v-table at the end of the cast check.
1780+
PerformPostCastAuthentication = CGF.getLangOpts().PointerAuthCalls;
1781+
CGPointerAuthInfo StrippingAuthInfo(0, PointerAuthenticationMode::Strip,
1782+
false, false, nullptr);
1783+
Address VTablePtrPtr = ThisAddr.withElementType(CGF.VoidPtrPtrTy);
1784+
VTable = CGF.Builder.CreateLoad(VTablePtrPtr, "vtable");
1785+
if (PerformPostCastAuthentication)
1786+
VTable = CGF.EmitPointerAuthAuth(StrippingAuthInfo, VTable);
1787+
} else
1788+
VTable = CGF.GetVTablePtr(ThisAddr, CGF.UnqualPtrTy, SrcDecl);
1789+
17431790
// Compare the vptr against the expected vptr for the destination type at
1744-
// this offset. Note that we do not know what type ThisAddr points to in
1745-
// the case where the derived class multiply inherits from the base class
1746-
// so we can't use GetVTablePtr, so we load the vptr directly instead.
1747-
llvm::Instruction *VPtr = CGF.Builder.CreateLoad(
1748-
ThisAddr.withElementType(CGF.VoidPtrPtrTy), "vtable");
1749-
CGM.DecorateInstructionWithTBAA(
1750-
VPtr, CGM.getTBAAVTablePtrAccessInfo(CGF.VoidPtrPtrTy));
1751-
llvm::Value *Success = CGF.Builder.CreateICmpEQ(
1752-
VPtr, getVTableAddressPoint(BaseSubobject(SrcDecl, *Offset), DestDecl));
1753-
llvm::Value *Result = ThisAddr.emitRawPointer(CGF);
1754-
if (!Offset->isZero())
1755-
Result = CGF.Builder.CreateInBoundsGEP(
1756-
CGF.CharTy, Result,
1757-
{llvm::ConstantInt::get(CGF.PtrDiffTy, -Offset->getQuantity())});
1791+
// this offset.
1792+
llvm::Constant *ExpectedVTable = getVTableAddressPoint(
1793+
BaseSubobject(SrcDecl, *ExactCastInfo.Offset), DestDecl);
1794+
llvm::Value *Success = CGF.Builder.CreateICmpEQ(VTable, ExpectedVTable);
1795+
llvm::Value *AdjustedThisPtr = ThisAddr.emitRawPointer(CGF);
1796+
1797+
if (!ExactCastInfo.Offset->isZero()) {
1798+
CharUnits::QuantityType Offset = ExactCastInfo.Offset->getQuantity();
1799+
llvm::Constant *OffsetConstant =
1800+
llvm::ConstantInt::get(CGF.PtrDiffTy, -Offset);
1801+
AdjustedThisPtr = CGF.Builder.CreateInBoundsGEP(CGF.CharTy, AdjustedThisPtr,
1802+
OffsetConstant);
1803+
PerformPostCastAuthentication = CGF.getLangOpts().PointerAuthCalls;
1804+
}
1805+
1806+
if (PerformPostCastAuthentication) {
1807+
// If we've changed the object pointer we authenticate the vtable pointer
1808+
// of the resulting object.
1809+
llvm::BasicBlock *NonNullBlock = CGF.Builder.GetInsertBlock();
1810+
llvm::BasicBlock *PostCastAuthSuccess =
1811+
CGF.createBasicBlock("dynamic_cast.postauth.success");
1812+
llvm::BasicBlock *PostCastAuthComplete =
1813+
CGF.createBasicBlock("dynamic_cast.postauth.complete");
1814+
CGF.Builder.CreateCondBr(Success, PostCastAuthSuccess,
1815+
PostCastAuthComplete);
1816+
CGF.EmitBlock(PostCastAuthSuccess);
1817+
Address AdjustedThisAddr =
1818+
Address(AdjustedThisPtr, CGF.IntPtrTy, CGF.getPointerAlign());
1819+
AuthenticateVTable(AdjustedThisAddr, DestDecl);
1820+
CGF.EmitBranch(PostCastAuthComplete);
1821+
CGF.EmitBlock(PostCastAuthComplete);
1822+
llvm::PHINode *PHI = CGF.Builder.CreatePHI(AdjustedThisPtr->getType(), 2);
1823+
PHI->addIncoming(AdjustedThisPtr, PostCastAuthSuccess);
1824+
llvm::Value *NullValue =
1825+
llvm::Constant::getNullValue(AdjustedThisPtr->getType());
1826+
PHI->addIncoming(NullValue, NonNullBlock);
1827+
AdjustedThisPtr = PHI;
1828+
}
17581829
CGF.Builder.CreateCondBr(Success, CastSuccess, CastFail);
1759-
return Result;
1830+
return AdjustedThisPtr;
17601831
}
17611832

17621833
llvm::Value *ItaniumCXXABI::emitDynamicCastToVoid(CodeGenFunction &CGF,

clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,19 @@ B *exact(A *a) {
1313
// EXACT-NOT: call {{.*}} @__dynamic_cast
1414
return dynamic_cast<B*>(a);
1515
}
16+
17+
struct C {
18+
virtual ~C();
19+
};
20+
21+
struct D final : private C {
22+
23+
};
24+
25+
// CHECK-LABEL: @_Z5exactP1C
26+
D *exact(C *a) {
27+
// INEXACT: call {{.*}} @__dynamic_cast
28+
// EXACT: entry:
29+
// EXACT-NEXT: ret ptr null
30+
return dynamic_cast<D*>(a);
31+
}

clang/test/CodeGenCXX/dynamic-cast-exact.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ struct E : A { int e; };
99
struct F : virtual A { int f; };
1010
struct G : virtual A { int g; };
1111
struct H final : C, D, E, F, G { int h; };
12+
struct H1 final: C, private D { int h1; };
1213

1314
// CHECK-LABEL: @_Z7inexactP1A
1415
C *inexact(A *a) {
@@ -77,10 +78,44 @@ H *exact_multi(A *a) {
7778
return dynamic_cast<H*>(a);
7879
}
7980

81+
// CHECK-LABEL: @_Z19exact_invalid_multiP1D
82+
H1 *exact_invalid_multi(D* d) {
83+
// CHECK: dynamic_cast.end:
84+
// CHECK-NEXT: ret ptr null
85+
return dynamic_cast<H1*>(d);
86+
}
87+
88+
// CHECK-LABEL: @_Z19exact_invalid_multiR1D
89+
H1 &exact_invalid_multi(D& d) {
90+
// CHECK: dynamic_cast.notnull:
91+
// CHECK-NEXT: br label %dynamic_cast.null
92+
// CHECK: dynamic_cast.null:
93+
// CHECK-NEXT: call void @__cxa_bad_cast()
94+
// CHECK-NEXT: unreachable
95+
return dynamic_cast<H1&>(d);
96+
}
97+
98+
namespace GH137518 {
99+
class base { virtual void fn() = 0; };
100+
class test final : base { virtual void fn() { } };
101+
test* new_test() { return new test(); }
102+
103+
// CHECK-LABEL: @_ZN8GH1375184castEPNS_4baseE(
104+
test* cast(base* b) {
105+
// CHECK: dynamic_cast.notnull:
106+
// CHECK: br label %dynamic_cast.null
107+
// CHECK: dynamic_cast.null:
108+
// CHECK: br label %dynamic_cast.end
109+
// CHECK: dynamic_cast.end:
110+
// CHECK: ret ptr null
111+
return dynamic_cast<test*>(b);
112+
}
113+
}
114+
80115
namespace GH64088 {
81116
// Ensure we mark the B vtable as used here, because we're going to emit a
82117
// reference to it.
83-
// CHECK: define {{.*}} @_ZN7GH640881BD0
118+
// CHECK: define {{.*}} void @_ZN7GH640881BD0Ev(
84119
struct A { virtual ~A(); };
85120
struct B final : A { virtual ~B() = default; };
86121
B *cast(A *p) { return dynamic_cast<B*>(p); }
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// RUN: %clang_cc1 -I%S %s -triple arm64e-apple-darwin10 -O1 -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination -fptrauth-vtable-pointer-type-discrimination -emit-llvm -std=c++11 -o - | FileCheck %s --check-prefixes=CHECK
2+
3+
struct A {
4+
virtual ~A();
5+
};
6+
struct B {
7+
int foo;
8+
virtual ~B();
9+
};
10+
struct C final : A, B {
11+
virtual void f(){};
12+
};
13+
struct D final : B, A {
14+
virtual void f(){};
15+
};
16+
17+
struct Offset {
18+
virtual ~Offset();
19+
};
20+
struct E {
21+
virtual ~E();
22+
};
23+
struct F final : Offset, E {
24+
};
25+
struct G {
26+
virtual ~G();
27+
int g;
28+
};
29+
struct H : E {
30+
int h;
31+
};
32+
struct I : E {
33+
int i;
34+
};
35+
struct J : virtual E {
36+
int j;
37+
};
38+
struct K : virtual E {
39+
int k;
40+
};
41+
struct L final : G, H, I, J, K {
42+
int l;
43+
};
44+
struct M final: G, private H { int m; };
45+
46+
// CHECK-LABEL: @_Z10exact_to_CP1A
47+
C *exact_to_C(A *a) {
48+
// CHECK: [[UNAUTHED_VPTR:%.*]] = load ptr, ptr %a, align 8
49+
// CHECK: [[VPTR_ADDRI:%.*]] = ptrtoint ptr %a to i64
50+
// CHECK: [[VPTR_ADDR_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 [[VPTR_ADDRI]], i64 62866)
51+
// CHECK: [[UNAUTHED_VPTRI:%.*]] = ptrtoint ptr [[UNAUTHED_VPTR]] to i64
52+
// CHECK: [[AUTHED_VPTRI:%.*]] = tail call i64 @llvm.ptrauth.auth(i64 [[UNAUTHED_VPTRI]], i32 2, i64 [[VPTR_ADDR_DISC]])
53+
// CHECK: [[IS_EXPECTED:%.*]] = icmp eq i64 [[AUTHED_VPTRI]], ptrtoint (ptr getelementptr inbounds nuw inrange(-16, 24) (i8, ptr @_ZTV1C, i64 16) to i64)
54+
// CHECK: br i1 [[IS_EXPECTED]], label %dynamic_cast.end, label %dynamic_cast.null
55+
// CHECK: [[NULL_CHECKED_RESULT:%.*]] = phi ptr [ %a, %dynamic_cast.notnull ], [ null, %dynamic_cast.null ]
56+
// CHECK: ret ptr [[NULL_CHECKED_RESULT]]
57+
return dynamic_cast<C*>(a);
58+
}
59+
60+
// CHECK-LABEL: @_Z9exact_t_DP1A
61+
D *exact_t_D(A *a) {
62+
// CHECK: dynamic_cast.notnull:
63+
// CHECK: [[SRC_UNAUTHED_VPTR:%.*]] = load ptr, ptr %a
64+
// CHECK: [[SRC_VPTR_ADDRI:%.*]] = ptrtoint ptr %a to i64
65+
// CHECK: [[SRC_VPTR_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 [[SRC_VPTR_ADDRI]], i64 62866)
66+
// CHECK: [[SRC_UNAUTHED_VPTRI:%.*]] = ptrtoint ptr [[SRC_UNAUTHED_VPTR]] to i64
67+
// CHECK: [[SRC_AUTHED_VPTRI:%.*]] = tail call i64 @llvm.ptrauth.auth(i64 [[SRC_UNAUTHED_VPTRI]], i32 2, i64 [[SRC_VPTR_DISC]])
68+
// CHECK: [[SUCCESS:%.*]] = icmp eq i64 [[SRC_AUTHED_VPTRI]], ptrtoint (ptr getelementptr inbounds nuw inrange(-16, 16) (i8, ptr @_ZTV1D, i64 56) to i64)
69+
// CHECK: br i1 [[SUCCESS]], label %dynamic_cast.postauth.success, label %dynamic_cast.postauth.complete
70+
// CHECK: dynamic_cast.postauth.success:
71+
// CHECK: [[ADJUSTED_THIS:%.*]] = getelementptr inbounds i8, ptr %a, i64 -16
72+
// CHECK: [[ADJUSTED_UNAUTHED_VPTR:%.*]] = load ptr, ptr [[ADJUSTED_THIS]]
73+
// CHECK: [[ADJUSTED_VPTR_ADDRI:%.*]] = ptrtoint ptr [[ADJUSTED_THIS]] to i64
74+
// CHECK: [[ADJUSTED_VPTR_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 [[ADJUSTED_VPTR_ADDRI]], i64 28965)
75+
// CHECK: [[ADJUSTED_UNAUTHED_VPTRI:%.*]] = ptrtoint ptr [[ADJUSTED_UNAUTHED_VPTR]] to i64
76+
// CHECK: [[ADJUSTED_AUTHED_VPTRI:%.*]] = tail call i64 @llvm.ptrauth.auth(i64 [[ADJUSTED_UNAUTHED_VPTRI]], i32 2, i64 [[ADJUSTED_VPTR_DISC]])
77+
// CHECK: [[ADJUSTED_AUTHED_VPTR:%.*]] = inttoptr i64 [[ADJUSTED_AUTHED_VPTRI]] to ptr
78+
// CHECK: br label %dynamic_cast.postauth.complete
79+
// CHECK: dynamic_cast.postauth.complete:
80+
// CHECK: [[AUTHED_ADJUSTED_THIS:%.*]] = phi ptr [ [[ADJUSTED_THIS]], %dynamic_cast.postauth.success ], [ null, %dynamic_cast.notnull ]
81+
// CHECK: br i1 [[SUCCESS]], label %dynamic_cast.end, label %dynamic_cast.null
82+
// CHECK: dynamic_cast.null:
83+
// CHECK: br label %dynamic_cast.end
84+
// CHECK: dynamic_cast.end:
85+
// CHECK: [[RESULT:%.*]] = phi ptr [ [[AUTHED_ADJUSTED_THIS]], %dynamic_cast.postauth.complete ], [ null, %dynamic_cast.null ]
86+
// CHECK: ret ptr [[RESULT]]
87+
return dynamic_cast<D*>(a);
88+
}
89+
90+
// CHECK-LABEL: @_Z11exact_multiP1E
91+
L *exact_multi(E *e) {
92+
// CHECK: dynamic_cast.notnull:
93+
// CHECK: [[VTABLE_ADDR:%.*]] = load ptr, ptr %e, align 8
94+
// CHECK: [[THIS_ADDRI:%.*]] = ptrtoint ptr %e to i64
95+
// CHECK: [[VTABLE_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 [[THIS_ADDRI]], i64 12810)
96+
// CHECK: [[VTABLE_ADDRI:%.*]] = ptrtoint ptr [[VTABLE_ADDR]] to i64
97+
// CHECK: [[AUTHED_VTABLEI:%.*]] = tail call i64 @llvm.ptrauth.auth(i64 [[VTABLE_ADDRI]], i32 2, i64 [[VTABLE_DISC]])
98+
// CHECK: [[AUTHED_VTABLE:%.*]] = inttoptr i64 [[AUTHED_VTABLEI]] to ptr
99+
// CHECK: [[PRIMARY_BASE_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[AUTHED_VTABLE]], i64 -16
100+
// CHECK: %offset.to.top = load i64, ptr [[PRIMARY_BASE_OFFSET]]
101+
// CHECK: [[ADJUSTED_THIS:%.*]] = getelementptr inbounds i8, ptr %e, i64 %offset.to.top
102+
// CHECK: [[ADJUSTED_THIS_VTABLE:%.*]] = load ptr, ptr [[ADJUSTED_THIS]]
103+
// CHECK: [[ADJUSTED_THIS_VTABLEI:%.*]] = ptrtoint ptr [[ADJUSTED_THIS_VTABLE]] to i64
104+
// CHECK: [[ADJUSTED_THIS_STRIPPED_VTABLEI:%.*]] = tail call i64 @llvm.ptrauth.strip(i64 [[ADJUSTED_THIS_VTABLEI]], i32 0)
105+
// CHECK: [[SUCCESS:%.*]] = icmp eq i64 [[ADJUSTED_THIS_STRIPPED_VTABLEI]], ptrtoint (ptr getelementptr inbounds nuw inrange(-24, 16) (i8, ptr @_ZTV1L, i64 24) to i64)
106+
// CHECK: br i1 [[SUCCESS]], label %dynamic_cast.postauth.success, label %dynamic_cast.postauth.complete
107+
// CHECK: dynamic_cast.postauth.success:
108+
// CHECK: [[ADJUSTED_THISI:%.*]] = ptrtoint ptr [[ADJUSTED_THIS]] to i64
109+
// CHECK: [[DEST_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 [[ADJUSTED_THISI]], i64 41434)
110+
// CHECK: tail call i64 @llvm.ptrauth.auth(i64 [[ADJUSTED_THIS_VTABLEI]], i32 2, i64 [[DEST_DISC]])
111+
// CHECK: br label %dynamic_cast.postauth.complete
112+
// CHECK: dynamic_cast.postauth.complete:
113+
// CHECK: [[AUTHED_ADJUSTED_THIS:%.*]] = phi ptr [ [[ADJUSTED_THIS]], %dynamic_cast.postauth.success ], [ null, %dynamic_cast.notnull ]
114+
// CHECK: br i1 [[SUCCESS]], label %dynamic_cast.end, label %dynamic_cast.null
115+
// CHECK: dynamic_cast.null:
116+
// CHECK: br label %dynamic_cast.end
117+
// CHECK: dynamic_cast.end:
118+
// CHECK: [[RESULT:%.*]] = phi ptr [ [[AUTHED_ADJUSTED_THIS]], %dynamic_cast.postauth.complete ], [ null, %dynamic_cast.null ]
119+
// CHECK: ret ptr [[RESULT]]
120+
return dynamic_cast<L*>(e);
121+
}
122+
123+
// CHECK-LABEL: @_Z19exact_invalid_multiP1H
124+
M *exact_invalid_multi(H* d) {
125+
// CHECK: dynamic_cast.notnull: ; preds = %entry
126+
// CHECK: [[VPTR:%.*]] = load ptr, ptr %d, align 8, !tbaa !2
127+
// CHECK: [[VPTR_ADDRI:%.*]] = ptrtoint ptr %d to i64
128+
// CHECK: [[VPTR_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 [[SRC_VPTR_ADDRI]], i64 12810)
129+
// CHECK: [[VPTRI:%.*]] = ptrtoint ptr [[VPTR]] to i64
130+
// CHECK: tail call i64 @llvm.ptrauth.auth(i64 [[VPTRI]], i32 2, i64 [[VPTR_DISC]])
131+
// CHECK: br label %dynamic_cast.end
132+
// CHECK: dynamic_cast.end:
133+
// CHECK: ret ptr null
134+
return dynamic_cast<M*>(d);
135+
}

0 commit comments

Comments
 (0)