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(Args)...); case QualType::PCK_ARCWeak: return asDerived().visitARCWeak(FT, std::forward(Args)...); + case QualType::PCK_PtrAuth: + return asDerived().visitPtrAuth(FT, std::forward(Args)...); case QualType::PCK_Struct: return asDerived().visitStruct(FT, std::forward(Args)...); case QualType::PCK_Trivial: diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 20ff529c7e0c6..b85fc45cf9562 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 67cd690af7499..1e4cb17e68a34 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2818,6 +2818,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; @@ -2830,7 +2833,7 @@ static bool isTriviallyCopyableTypeImpl(const QualType &type, return ClassDecl->isTriviallyCopyable(); } } - return true; + return !RT->getDecl()->isNonTrivialToPrimitiveCopy(); } // No other types can match. return false; @@ -2968,6 +2971,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(E) && cast(E)->getCastKind() == CK_LValueToRValue && - !type->isArrayParameterType()) { + !type->isArrayParameterType() && !type.isNonTrivialToPrimitiveCopy()) { LValue L = EmitLValue(cast(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, 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, RValue SrcVal = this->CGF->EmitLoadOfLValue(SrcLV, SourceLocation()); this->CGF->EmitStoreThroughLValue(SrcVal, DstLV); } + void visitPtrAuth(QualType FT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array 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()->getDecl()->fields()) visit(FD->getType(), FD->getLocation()); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 46933c5c43168..3b42cdc4def46 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 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(); +constexpr int matching_ptrauth = + test_ptrauth_conflict(); +constexpr int mismatching_ptrauth = + test_ptrauth_conflict(); // #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' 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}}