Author: Oliver Hunt Date: 2025-04-24T16:22:50-07:00 New Revision: 5b16941f5707c3a326b688e0954f8882d8a36951
URL: https://github.com/llvm/llvm-project/commit/5b16941f5707c3a326b688e0954f8882d8a36951 DIFF: https://github.com/llvm/llvm-project/commit/5b16941f5707c3a326b688e0954f8882d8a36951.diff LOG: [clang] Ensure correct copying of records with authenticated fields (#136783) When records contain fields with pointer authentication, even simple copies can require additional work be performed. This patch contains the core functionality required to handle user defined structs, as well as the implicitly constructed structs for blocks, etc. Co-authored-by: Ahmed Bougacha Co-authored-by: Akira Hatanaka Co-authored-by: John Mccall Added: clang/test/CodeGen/ptrauth-in-c-struct.c clang/test/CodeGen/ptrauth-qualifier-blocks.c Modified: clang/include/clang/AST/NonTrivialTypeVisitor.h clang/include/clang/AST/Type.h clang/lib/AST/ASTContext.cpp clang/lib/AST/Type.cpp clang/lib/CodeGen/CGBlocks.cpp clang/lib/CodeGen/CGBlocks.h clang/lib/CodeGen/CGCall.cpp clang/lib/CodeGen/CGNonTrivialStruct.cpp clang/lib/Sema/SemaChecking.cpp clang/lib/Sema/SemaDecl.cpp clang/test/Sema/ptrauth-qualifier.c clang/test/SemaCXX/ptrauth-qualifier.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/NonTrivialTypeVisitor.h b/clang/include/clang/AST/NonTrivialTypeVisitor.h index cf320c8a478af..80f247347f1b3 100644 --- a/clang/include/clang/AST/NonTrivialTypeVisitor.h +++ b/clang/include/clang/AST/NonTrivialTypeVisitor.h @@ -93,6 +93,8 @@ struct CopiedTypeVisitor { return asDerived().visitARCStrong(FT, std::forward<Ts>(Args)...); case QualType::PCK_ARCWeak: return asDerived().visitARCWeak(FT, std::forward<Ts>(Args)...); + case QualType::PCK_PtrAuth: + return asDerived().visitPtrAuth(FT, std::forward<Ts>(Args)...); case QualType::PCK_Struct: return asDerived().visitStruct(FT, std::forward<Ts>(Args)...); case QualType::PCK_Trivial: diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 86d43e1a05b55..3e1fb05ad537c 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1507,6 +1507,9 @@ class QualType { /// with the ARC __weak qualifier. PCK_ARCWeak, + /// The type is an address-discriminated signed pointer type. + PCK_PtrAuth, + /// The type is a struct containing a field whose type is neither /// PCK_Trivial nor PCK_VolatileTrivial. /// Note that a C++ struct type does not necessarily match this; C++ copying diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 2836d68b05ff6..b1ecd9d63702b 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -8372,6 +8372,9 @@ bool ASTContext::BlockRequiresCopying(QualType Ty, return true; } + if (Ty.hasAddressDiscriminatedPointerAuth()) + return true; + // The block needs copy/destroy helpers if Ty is non-trivial to destructively // move or destroy. if (Ty.isNonTrivialToPrimitiveDestructiveMove() || Ty.isDestructedType()) diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 08be90eab6537..111a642173418 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2817,6 +2817,9 @@ static bool isTriviallyCopyableTypeImpl(const QualType &type, if (CanonicalType->isIncompleteType()) return false; + if (CanonicalType.hasAddressDiscriminatedPointerAuth()) + return false; + // As an extension, Clang treats vector types as Scalar types. if (CanonicalType->isScalarType() || CanonicalType->isVectorType()) return true; @@ -2829,7 +2832,7 @@ static bool isTriviallyCopyableTypeImpl(const QualType &type, return ClassDecl->isTriviallyCopyable(); } } - return true; + return !RT->getDecl()->isNonTrivialToPrimitiveCopy(); } // No other types can match. return false; @@ -2967,6 +2970,8 @@ QualType::PrimitiveCopyKind QualType::isNonTrivialToPrimitiveCopy() const { case Qualifiers::OCL_Weak: return PCK_ARCWeak; default: + if (hasAddressDiscriminatedPointerAuth()) + return PCK_PtrAuth; return Qs.hasVolatile() ? PCK_VolatileTrivial : PCK_Trivial; } } diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index faef6a5fbe1f5..ba0d87fdc5d43 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -1591,6 +1591,10 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags); } + if (T.hasAddressDiscriminatedPointerAuth()) + return std::make_pair( + BlockCaptureEntityKind::AddressDiscriminatedPointerAuth, Flags); + Flags = BLOCK_FIELD_IS_OBJECT; bool isBlockPointer = T->isBlockPointerType(); if (isBlockPointer) @@ -1611,6 +1615,10 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, return std::make_pair(!isBlockPointer ? BlockCaptureEntityKind::ARCStrong : BlockCaptureEntityKind::BlockObject, Flags); + case QualType::PCK_PtrAuth: + return std::make_pair( + BlockCaptureEntityKind::AddressDiscriminatedPointerAuth, + BlockFieldFlags()); case QualType::PCK_Trivial: case QualType::PCK_VolatileTrivial: { if (!T->isObjCRetainableType()) @@ -1713,6 +1721,13 @@ static std::string getBlockCaptureStr(const CGBlockInfo::Capture &Cap, case BlockCaptureEntityKind::ARCStrong: Str += "s"; break; + case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: { + auto PtrAuth = CaptureTy.getPointerAuth(); + assert(PtrAuth && PtrAuth.isAddressDiscriminated()); + Str += "p" + llvm::to_string(PtrAuth.getKey()) + "d" + + llvm::to_string(PtrAuth.getExtraDiscriminator()); + break; + } case BlockCaptureEntityKind::BlockObject: { const VarDecl *Var = CI.getVariable(); unsigned F = Flags.getBitMask(); @@ -1829,6 +1844,7 @@ static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, } break; } + case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: case BlockCaptureEntityKind::None: break; } @@ -1925,6 +1941,14 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { case BlockCaptureEntityKind::ARCWeak: EmitARCCopyWeak(dstField, srcField); break; + case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: { + QualType Type = CI.getVariable()->getType(); + PointerAuthQualifier PointerAuth = Type.getPointerAuth(); + assert(PointerAuth && PointerAuth.isAddressDiscriminated()); + EmitPointerAuthCopy(PointerAuth, Type, dstField, srcField); + // We don't need to push cleanups for ptrauth types. + continue; + } case BlockCaptureEntityKind::NonTrivialCStruct: { // If this is a C struct that requires non-trivial copy construction, // emit a call to its copy constructor. @@ -2261,6 +2285,33 @@ class CXXByrefHelpers final : public BlockByrefHelpers { } }; +/// Emits the copy/dispose helpers for a __block variable with +/// address-discriminated pointer authentication. +class AddressDiscriminatedByrefHelpers final : public BlockByrefHelpers { + QualType VarType; + +public: + AddressDiscriminatedByrefHelpers(CharUnits Alignment, QualType Type) + : BlockByrefHelpers(Alignment), VarType(Type) { + assert(Type.hasAddressDiscriminatedPointerAuth()); + } + + void emitCopy(CodeGenFunction &CGF, Address DestField, + Address SrcField) override { + CGF.EmitPointerAuthCopy(VarType.getPointerAuth(), VarType, DestField, + SrcField); + } + + bool needsDispose() const override { return false; } + void emitDispose(CodeGenFunction &CGF, Address Field) override { + llvm_unreachable("should never be called"); + } + + void profileImpl(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(VarType.getCanonicalType().getAsOpaquePtr()); + } +}; + /// Emits the copy/dispose helpers for a __block variable that is a non-trivial /// C struct. class NonTrivialCStructByrefHelpers final : public BlockByrefHelpers { @@ -2462,7 +2513,10 @@ CodeGenFunction::buildByrefHelpers(llvm::StructType &byrefType, return ::buildByrefHelpers( CGM, byrefInfo, CXXByrefHelpers(valueAlignment, type, copyExpr)); } - + if (type.hasAddressDiscriminatedPointerAuth()) { + return ::buildByrefHelpers( + CGM, byrefInfo, AddressDiscriminatedByrefHelpers(valueAlignment, type)); + } // If type is a non-trivial C struct type that is non-trivial to // destructly move or destroy, build the copy and dispose helpers. if (type.isNonTrivialToPrimitiveDestructiveMove() == QualType::PCK_Struct || diff --git a/clang/lib/CodeGen/CGBlocks.h b/clang/lib/CodeGen/CGBlocks.h index 8d10c4f69b202..b2e850046ab9a 100644 --- a/clang/lib/CodeGen/CGBlocks.h +++ b/clang/lib/CodeGen/CGBlocks.h @@ -146,6 +146,7 @@ class BlockByrefInfo { enum class BlockCaptureEntityKind { None, CXXRecord, // Copy or destroy + AddressDiscriminatedPointerAuth, ARCWeak, ARCStrong, NonTrivialCStruct, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 8cb27420dd911..836f34e5b347d 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4857,7 +4857,7 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, if (HasAggregateEvalKind && isa<ImplicitCastExpr>(E) && cast<CastExpr>(E)->getCastKind() == CK_LValueToRValue && - !type->isArrayParameterType()) { + !type->isArrayParameterType() && !type.isNonTrivialToPrimitiveCopy()) { LValue L = EmitLValue(cast<CastExpr>(E)->getSubExpr()); assert(L.isSimple()); args.addUncopiedAggregate(L, type); diff --git a/clang/lib/CodeGen/CGNonTrivialStruct.cpp b/clang/lib/CodeGen/CGNonTrivialStruct.cpp index c634b5c010e2d..e0983ef256e71 100644 --- a/clang/lib/CodeGen/CGNonTrivialStruct.cpp +++ b/clang/lib/CodeGen/CGNonTrivialStruct.cpp @@ -266,6 +266,18 @@ struct GenBinaryFuncName : CopyStructVisitor<GenBinaryFuncName<IsMove>, IsMove>, this->appendStr("_tv" + llvm::to_string(OffsetInBits) + "w" + llvm::to_string(getFieldSize(FD, FT, this->Ctx))); } + + void visitPtrAuth(QualType FT, const FieldDecl *FD, + CharUnits CurStructOffset) { + this->appendStr("_pa"); + PointerAuthQualifier PtrAuth = FT.getPointerAuth().withoutKeyNone(); + this->appendStr(llvm::to_string(PtrAuth.getKey()) + "_"); + this->appendStr(llvm::to_string(PtrAuth.getExtraDiscriminator()) + "_"); + if (PtrAuth.authenticatesNullValues()) + this->appendStr("anv_"); + CharUnits FieldOffset = CurStructOffset + this->getFieldOffset(FD); + this->appendStr(llvm::to_string(FieldOffset.getQuantity())); + } }; struct GenDefaultInitializeFuncName @@ -568,6 +580,13 @@ struct GenBinaryFunc : CopyStructVisitor<Derived, IsMove>, RValue SrcVal = this->CGF->EmitLoadOfLValue(SrcLV, SourceLocation()); this->CGF->EmitStoreThroughLValue(SrcVal, DstLV); } + void visitPtrAuth(QualType FT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array<Address, 2> Addrs) { + PointerAuthQualifier PtrAuth = FT.getPointerAuth().withoutKeyNone(); + Addrs[DstIdx] = this->getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = this->getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + this->CGF->EmitPointerAuthCopy(PtrAuth, FT, Addrs[DstIdx], Addrs[SrcIdx]); + } }; // These classes that emit the special functions for a non-trivial struct. diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index b2b26a2c39cf2..9c9372d9ee2b0 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -9443,6 +9443,9 @@ struct SearchNonTrivialToCopyField void visitARCWeak(QualType FT, SourceLocation SL) { S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0); } + void visitPtrAuth(QualType FT, SourceLocation SL) { + S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0); + } void visitStruct(QualType FT, SourceLocation SL) { for (const FieldDecl *FD : FT->castAs<RecordType>()->getDecl()->fields()) visit(FD->getType(), FD->getLocation()); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d28a2107d58a9..bd99d49122b4e 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13395,6 +13395,12 @@ struct DiagNonTrivalCUnionCopyVisitor asDerived().visit(FD->getType(), FD, InNonTrivialUnion); } + void visitPtrAuth(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) { + if (InNonTrivialUnion) + S.Diag(FD->getLocation(), diag::note_non_trivial_c_union) + << 1 << 2 << QT << FD->getName(); + } + void preVisit(QualType::PrimitiveCopyKind PCK, QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {} void visitTrivial(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {} diff --git a/clang/test/CodeGen/ptrauth-in-c-struct.c b/clang/test/CodeGen/ptrauth-in-c-struct.c new file mode 100644 index 0000000000000..d415c18d0da58 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-in-c-struct.c @@ -0,0 +1,205 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fblocks -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -emit-llvm -o - %s | FileCheck %s + +#define AQ1_50 __ptrauth(1,1,50) +#define AQ2_30 __ptrauth(2,1,30) +#define IQ __ptrauth(1,0,50) + +typedef void (^BlockTy)(void); + +// CHECK: %[[STRUCT_SA:.*]] = type { i32, ptr } +// CHECK: %[[STRUCT_SA2:.*]] = type { i32, ptr } +// CHECK: %[[STRUCT_SI:.*]] = type { ptr } + +typedef struct { + int f0; + int * AQ1_50 f1; // Signed using address discrimination. +} SA; + +typedef struct { + int f0; + int * AQ2_30 f1; // Signed using address discrimination. +} SA2; + +typedef struct { + int * IQ f; // No address discrimination. +} SI; + +typedef struct { + // Transitively includes an address discriminated value + SA nested; +} Nested_AddrDiscrimination; + +typedef struct { + // Transitively includes a pointer to a struct containing + // an address discriminated value, which means that this + // does not actually contain an address discriminated value + SA *nestedPtr; +} Nested_PtrAddrDiscrimination; + +SA getSA(void); +void calleeSA(SA); + +int g0; + +// CHECK: define void @test_copy_constructor_SA(ptr noundef %{{.*}}) +// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8( + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa1_50_8(ptr noundef %[[DST:.*]], ptr noundef %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[DST]], ptr %[[DST_ADDR]], align 8 +// CHECK: store ptr %[[SRC]], ptr %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[SRC_ADDR]], align 8 +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 8 +// CHECK: %[[V9:.*]] = getelementptr inbounds i8, ptr %[[V1]], i64 8 +// CHECK: %[[V11:.*]] = load ptr, ptr %[[V9]], align 8 +// CHECK: %[[V12:.*]] = ptrtoint ptr %[[V9]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V12]], i64 50) +// CHECK: %[[V14:.*]] = ptrtoint ptr %[[V6]] to i64 +// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V14]], i64 50) +// CHECK: %[[V17:.*]] = ptrtoint ptr %[[V11]] to i64 +// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V17]], i32 1, i64 %[[V13]], i32 1, i64 %[[V15]]) + +void test_copy_constructor_SA(SA *s) { + SA t = *s; +} + +// CHECK: define void @test_copy_constructor_SA2(ptr noundef %{{.*}}) +// CHECK: call void @__copy_constructor_8_8_t0w4_pa2_30_8( + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa2_30_8(ptr noundef %[[DST:.*]], ptr noundef %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[DST]], ptr %[[DST_ADDR]], align 8 +// CHECK: store ptr %[[SRC]], ptr %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[SRC_ADDR]], align 8 +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 8 +// CHECK: %[[V9:.*]] = getelementptr inbounds i8, ptr %[[V1]], i64 8 +// CHECK: %[[V11:.*]] = load ptr, ptr %[[V9]], align 8 +// CHECK: %[[V12:.*]] = ptrtoint ptr %[[V9]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V12]], i64 30) +// CHECK: %[[V14:.*]] = ptrtoint ptr %[[V6]] to i64 +// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V14]], i64 30) +// CHECK: %[[V17:.*]] = ptrtoint ptr %[[V11]] to i64 +// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V17]], i32 2, i64 %[[V13]], i32 2, i64 %[[V15]]) + +void test_copy_constructor_SA2(SA2 *s) { + SA2 t = *s; +} + +// CHECK: define void @test_copy_assignment_SA( +// CHECK: call void @__copy_assignment_8_8_t0w4_pa1_50_8( + +// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_t0w4_pa1_50_8( + +void test_copy_assignment_SA(SA *d, SA *s) { + *d = *s; +} + +// CHECK: define void @test_move_constructor_SA( +// CHECK: define internal void @__Block_byref_object_copy_( +// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_t0w4_pa1_50_8( + +void test_move_constructor_SA(void) { + __block SA t; + BlockTy b = ^{ (void)t; }; +} + +// CHECK: define void @test_move_assignment_SA( +// CHECK: call void @__move_assignment_8_8_t0w4_pa1_50_8( +// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_t0w4_pa1_50_8( + +void test_move_assignment_SA(SA *p) { + *p = getSA(); +} + +// CHECK: define void @test_parameter_SA(ptr noundef %{{.*}}) +// CHECK-NOT: call +// CHECK: ret void + +void test_parameter_SA(SA a) { +} + +// CHECK: define void @test_argument_SA(ptr noundef %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SA]], align 8 +// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8(ptr %[[AGG_TMP]], ptr %[[V0]]) +// CHECK: call void @calleeSA(ptr noundef %[[AGG_TMP]]) +// CHECK-NOT: call +// CHECK: ret void + +void test_argument_SA(SA *a) { + calleeSA(*a); +} + +// CHECK: define void @test_return_SA(ptr dead_on_unwind noalias writable sret(%struct.SA) align 8 %[[AGG_RESULT:.*]], ptr noundef %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8(ptr %[[AGG_RESULT]], ptr %[[V0]]) +// CHECK-NOT: call +// CHECK: ret void + +SA test_return_SA(SA *a) { + return *a; +} + +// CHECK: define void @test_copy_constructor_SI( +// CHECK-NOT: call +// CHECK: call void @llvm.memcpy.p0.p0.i64( +// CHECK-NOT: call +// CHECK: ret void + +void test_copy_constructor_SI(SI *s) { + SI t = *s; +} + +// CHECK: define void @test_parameter_SI(i64 %{{.*}}) +// CHECK-NOT: call +// CHECK: ret void + +void test_parameter_SI(SI a) { +} + +// CHECK-LABEL: define void @test_array( +// CHECK: %[[F1:.*]] = getelementptr inbounds nuw %[[STRUCT_SA]], ptr %{{.*}}, i32 0, i32 1 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[F1]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V0]], i64 50) +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 %[[V1]]) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: store ptr %[[V3]], ptr %[[F1]], align 8 +// CHECK: %[[F12:.*]] = getelementptr inbounds nuw %[[STRUCT_SA]], ptr %{{.*}}, i32 0, i32 1 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[F12]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V4]], i64 50) +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 %[[V5]]) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: store ptr %[[V7]], ptr %[[F12]], align 8 + +void test_array(void) { + const SA a[] = {{0, &g0}, {1, &g0}}; +} + + +void test_nested_struct(Nested_AddrDiscrimination* Src) { + Nested_AddrDiscrimination Dst = *Src; +} +// CHECK-LABEL: define void @test_nested_struct +// CHECK: [[DST:%.*]] = alloca %struct.Nested_AddrDiscrimination +// CHECK: [[SRC_ADDR:%.*]] = load ptr, ptr %Src.addr +// CHECK: call void @__copy_constructor_8_8_S_t0w4_pa1_50_8(ptr [[DST]], ptr [[SRC_ADDR]]) + +// CHECK-LABEL: define linkonce_odr hidden void @__copy_constructor_8_8_S_t0w4_pa1_50_8( +// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8 + + +void test_nested_struct_ptr(Nested_PtrAddrDiscrimination* Src) { + Nested_PtrAddrDiscrimination Dst = *Src; +} +// CHECK-LABEL: define void @test_nested_struct_ptr +// CHECK: [[DST:%.*]] = alloca %struct.Nested_PtrAddrDiscrimination +// CHECK: [[SRC_ADDR:%.*]] = load ptr, ptr %Src.addr +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[DST]], ptr align 8 [[SRC_ADDR]], i64 8, i1 false) diff --git a/clang/test/CodeGen/ptrauth-qualifier-blocks.c b/clang/test/CodeGen/ptrauth-qualifier-blocks.c new file mode 100644 index 0000000000000..62da59cf327f6 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-qualifier-blocks.c @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -fblocks -emit-llvm %s -o - | FileCheck %s + +struct A { + int value; +}; +struct A *createA(void); + +void use_block(int (^)(void)); + +// CHECK-LABEL: define void @test_block_nonaddress_capture( +void test_block_nonaddress_capture() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK: [[BLOCK:%.*]] = alloca + // flags - no copy/dispose required + // CHECK: store i32 1073741824, ptr + // CHECK: [[CAPTURE:%.*]] = getelementptr inbounds {{.*}} [[BLOCK]], i32 0, i32 5 + // CHECK: [[LOAD:%.*]] = load ptr, ptr [[VAR]], + // CHECK: store ptr [[LOAD]], ptr [[CAPTURE]] + struct A * __ptrauth(1, 0, 15) ptr = createA(); + use_block(^{ return ptr->value; }); +} +// CHECK-LABEL: define internal i32 @__test_block_nonaddress_capture_block_invoke +// CHECK: call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 1, i64 15) + +// CHECK-LABEL: define void @test_block_address_capture( +void test_block_address_capture() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK: [[BLOCK:%.*]] = alloca + // flags - copy/dispose required + // CHECK: store i32 1107296256, ptr + // CHECK: [[CAPTURE:%.*]] = getelementptr inbounds {{.*}} [[BLOCK]], i32 0, i32 5 + // CHECK: [[LOAD:%.*]] = load ptr, ptr [[VAR]], + // CHECK: [[T0:%.*]] = ptrtoint ptr [[VAR]] to i64 + // CHECK: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30) + // CHECK: [[T0:%.*]] = ptrtoint ptr [[CAPTURE]] to i64 + // CHECK: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30) + // CHECK: [[T0:%.*]] = icmp ne ptr [[LOAD]], null + // CHECK: br i1 [[T0]] + // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) + // CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK: [[T0:%.*]] = phi + // CHECK: store ptr [[T0]], ptr [[CAPTURE]] + struct A * __ptrauth(1, 1, 30) ptr = createA(); + use_block(^{ return ptr->value; }); +} +// CHECK-LABEL: define internal i32 @__test_block_address_capture_block_invoke +// CHECK: call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 1, i64 {{%.*}}) + +// CHECK: linkonce_odr hidden void @__copy_helper_block_8_32p1d30( +// CHECK: [[OLDSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 5 +// CHECK: [[NEWSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 5 +// CHECK: [[LOAD:%.*]] = load ptr, ptr [[OLDSLOT]], +// CHECK: [[T0:%.*]] = ptrtoint ptr [[OLDSLOT]] to i64 +// CHECK: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30) +// CHECK: [[T0:%.*]] = ptrtoint ptr [[NEWSLOT]] to i64 +// CHECK: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30) +// CHECK: [[T0:%.*]] = icmp ne ptr [[LOAD]], null +// CHECK: br i1 [[T0]] +// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 +// CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr +// CHECK: [[T0:%.*]] = phi +// CHECK: store ptr [[T0]], ptr [[NEWSLOT]] + +// CHECK-LABEL: define void @test_block_nonaddress_byref_capture( +void test_block_nonaddress_byref_capture() { + // flags - no copy/dispose required for byref + // CHECK: store i32 0, + // CHECK: call ptr @createA() + // flags - copy/dispose required for block (because it captures byref) + // CHECK: store i32 1107296256, + __block struct A * __ptrauth(1, 0, 45) ptr = createA(); + use_block(^{ return ptr->value; }); +} + +// CHECK-LABEL: define void @test_block_address_byref_capture( +void test_block_address_byref_capture() { + // CHECK: [[BYREF:%.*]] = alloca [[BYREF_T:.*]], align + // CHECK: [[BLOCK:%.*]] = alloca + // flags - byref requires copy/dispose + // CHECK: store i32 33554432, + // CHECK: store i32 48, + // CHECK: [[COPY_HELPER_FIELD:%.*]] = getelementptr inbounds nuw [[BYREF_T]], ptr [[BYREF]], i32 0, i32 4 + // CHECK: store ptr @__Block_byref_object_copy_, ptr [[COPY_HELPER_FIELD]], align + // CHECK: [[DISPOSE_HELPER_FIELD:%.*]] = getelementptr inbounds nuw [[BYREF_T]], ptr [[BYREF]], i32 0, i32 5 + // CHECK: store ptr @__Block_byref_object_dispose_, ptr [[DISPOSE_HELPER_FIELD]], align + // flags - copy/dispose required + // CHECK: store i32 1107296256, ptr + __block struct A * __ptrauth(1, 1, 60) ptr = createA(); + use_block(^{ return ptr->value; }); +} +// CHECK-LABEL: define internal void @__Block_byref_object_copy_ +// CHECK: [[NEWSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 6 +// CHECK: [[OLDSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 6 +// CHECK: [[LOAD:%.*]] = load ptr, ptr [[OLDSLOT]], +// CHECK: [[T0:%.*]] = ptrtoint ptr [[OLDSLOT]] to i64 +// CHECK: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 60) +// CHECK: [[T0:%.*]] = ptrtoint ptr [[NEWSLOT]] to i64 +// CHECK: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 60) +// CHECK: [[T0:%.*]] = icmp ne ptr [[LOAD]], null +// CHECK: br i1 [[T0]] +// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 +// CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr +// CHECK: [[T0:%.*]] = phi +// CHECK: store ptr [[T0]], ptr [[NEWSLOT]] diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c index ab12acd8975f4..2ec4471e519ca 100644 --- a/clang/test/Sema/ptrauth-qualifier.c +++ b/clang/test/Sema/ptrauth-qualifier.c @@ -103,3 +103,45 @@ static_assert(_Generic(typeof(overload_func(&ptr0)), int : 1, default : 0)); static_assert(_Generic(typeof(overload_func(&valid0)), float : 1, default : 0)); void func(int array[__ptrauth(VALID_DATA_KEY) 10]); // expected-error {{'__ptrauth' qualifier only applies to pointer types; 'int[10]' is invalid}} + +struct S0 { // expected-note 4 {{struct S0' has subobjects that are non-trivial to copy}} + intp __ptrauth(1, 1, 50) f0; // expected-note 4 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}} +}; + +union U0 { // expected-note 4 {{union U0' has subobjects that are non-trivial to copy}} + struct S0 s0; +}; + +struct S1 { + intp __ptrauth(1, 0, 50) f0; +}; + +union U1 { + struct S1 s1; +}; + +union U2 { // expected-note 2 {{union U2' has subobjects that are non-trivial to copy}} + intp __ptrauth(1, 1, 50) f0; // expected-note 2 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}} + intp __ptrauth(1, 0, 50) f1; +}; + +struct S2 { // expected-note 2 {{struct S2' has subobjects that are non-trivial to copy}} + intp __ptrauth(1, 1, 50) f0[4]; // expected-note 2 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}} +}; + +union U3 { // expected-note 2 {{union U3' has subobjects that are non-trivial to copy}} + struct S2 s2; +}; + +struct S4 { + union U0 u0; +}; + +union U0 foo0(union U0); // expected-error {{cannot use type 'union U0' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U0' for a function/method parameter since it is a union that is non-trivial to copy}} +union U1 foo1(union U1); + +union U2 foo2(union U2); // expected-error {{cannot use type 'union U2' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U2' for a function/method parameter since it is a union that is non-trivial to copy}} + +union U3 foo3(union U3); // expected-error {{cannot use type 'union U3' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U3' for a function/method parameter since it is a union that is non-trivial to copy}} + +struct S4 foo4(struct S4); // expected-error {{cannot use type 'struct S4' for function/method return since it contains a union that is non-trivial to copy}} expected-error {{cannot use type 'struct S4' for a function/method parameter since it contains a union that is non-trivial to copy}} diff --git a/clang/test/SemaCXX/ptrauth-qualifier.cpp b/clang/test/SemaCXX/ptrauth-qualifier.cpp index a7dc6ae2ffe86..4a164d67f2559 100644 --- a/clang/test/SemaCXX/ptrauth-qualifier.cpp +++ b/clang/test/SemaCXX/ptrauth-qualifier.cpp @@ -211,3 +211,21 @@ namespace test_concept { func(&p2); // expected-error {{no matching function for call to 'func'}} } } + +template <class A, class B> constexpr int test_ptrauth_conflict() { + A *a = nullptr; + B *b = nullptr; + a = b; // #BtoA + b = a; // #AtoB + return 0; +} + +constexpr int no_ptrauth = test_ptrauth_conflict<int *, int *>(); +constexpr int matching_ptrauth = + test_ptrauth_conflict<int * __ptrauth(1,1,123), int * __ptrauth(1,1,123)>(); +constexpr int mismatching_ptrauth = + test_ptrauth_conflict<int * __ptrauth(2,1,1024), int * __ptrauth(1,1,123)>(); // #FailedConstExpr +// expected-error@#FailedConstExpr {{constexpr variable 'mismatching_ptrauth' must be initialized by a constant expression}} +// expected-note@#FailedConstExpr {{in instantiation of function template specialization 'test_ptrauth_conflict<int *__ptrauth(2,1,1024), int *__ptrauth(1,1,123)>' requested here}} +// expected-error@#BtoA {{assigning 'int *__ptrauth(1,1,123) *' to 'int *__ptrauth(2,1,1024) *' changes pointer authentication of pointee type}} +// expected-error@#AtoB {{assigning 'int *__ptrauth(2,1,1024) *' to 'int *__ptrauth(1,1,123) *' changes pointer authentication of pointee type}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits