Skip to content

Commit 1dac5d4

Browse files
committed
Support for address discriminated pointers
1 parent 5d95667 commit 1dac5d4

File tree

12 files changed

+241
-43
lines changed

12 files changed

+241
-43
lines changed

include/swift/AST/Decl.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -613,13 +613,16 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
613613
IsActor : 1
614614
);
615615

616-
SWIFT_INLINE_BITFIELD(
617-
StructDecl, NominalTypeDecl, 1 + 1,
618-
/// True if this struct has storage for fields that aren't accessible in
619-
/// Swift.
620-
HasUnreferenceableStorage : 1,
621-
/// True if this struct is imported from C++ and does not have trivial value witness functions.
622-
IsCxxNonTrivial : 1);
616+
SWIFT_INLINE_BITFIELD(StructDecl, NominalTypeDecl, 1 + 1 + 1,
617+
/// True if this struct has storage for fields that
618+
/// aren't accessible in Swift.
619+
HasUnreferenceableStorage : 1,
620+
/// True if this struct is imported from C++ and does
621+
/// not have trivial value witness functions.
622+
IsCxxNonTrivial : 1,
623+
/// True if this struct is imported from C and has
624+
/// address diversified ptrauth qualified field.
625+
IsNonTrivialPtrAuth : 1);
623626

624627
SWIFT_INLINE_BITFIELD(EnumDecl, NominalTypeDecl, 2+1,
625628
/// True if the enum has cases and at least one case has associated values.
@@ -4267,6 +4270,14 @@ class StructDecl final : public NominalTypeDecl {
42674270

42684271
void setIsCxxNonTrivial(bool v) { Bits.StructDecl.IsCxxNonTrivial = v; }
42694272

4273+
bool isNonTrivialPtrAuth() const {
4274+
return Bits.StructDecl.IsNonTrivialPtrAuth;
4275+
}
4276+
4277+
void setHasNonTrivialPtrAuth(bool v) {
4278+
Bits.StructDecl.IsNonTrivialPtrAuth = v;
4279+
}
4280+
42704281
Type getTemplateInstantiationType() const { return TemplateInstantiationType; }
42714282
void setTemplateInstantiationType(Type t) { TemplateInstantiationType = t; }
42724283
};

lib/ClangImporter/ImportDecl.cpp

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,26 +2024,45 @@ namespace {
20242024
return nullptr;
20252025
}
20262026

2027+
auto isNonTrivialDueToAddressDiversifiedPtrAuth =
2028+
[](const clang::RecordDecl *decl) {
2029+
for (auto *field : decl->fields()) {
2030+
if (!field->getType().isNonTrivialToPrimitiveCopy()) {
2031+
continue;
2032+
}
2033+
if (field->getType().isNonTrivialToPrimitiveCopy() !=
2034+
clang::QualType::PCK_PtrAuth) {
2035+
return false;
2036+
}
2037+
}
2038+
return true;
2039+
};
2040+
2041+
bool isNonTrivialPtrAuth = false;
20272042
// FIXME: We should actually support strong ARC references and similar in
20282043
// C structs. That'll require some SIL and IRGen work, though.
20292044
if (decl->isNonTrivialToPrimitiveCopy() ||
20302045
decl->isNonTrivialToPrimitiveDestroy()) {
2031-
// Note that there is a third predicate related to these,
2032-
// isNonTrivialToPrimitiveDefaultInitialize. That one's not important
2033-
// for us because Swift never "trivially default-initializes" a struct
2034-
// (i.e. uses whatever bits were lying around as an initial value).
2035-
2036-
// FIXME: It would be nice to instead import the declaration but mark
2037-
// it as unavailable, but then it might get used as a type for an
2038-
// imported function and the developer would be able to use it without
2039-
// referencing the name, which would sidestep our availability
2040-
// diagnostics.
2041-
Impl.addImportDiagnostic(
2042-
decl, Diagnostic(
2043-
diag::record_non_trivial_copy_destroy,
2044-
Impl.SwiftContext.AllocateCopy(decl->getNameAsString())),
2045-
decl->getLocation());
2046-
return nullptr;
2046+
isNonTrivialPtrAuth = isNonTrivialDueToAddressDiversifiedPtrAuth(decl);
2047+
if (!isNonTrivialPtrAuth) {
2048+
// Note that there is a third predicate related to these,
2049+
// isNonTrivialToPrimitiveDefaultInitialize. That one's not important
2050+
// for us because Swift never "trivially default-initializes" a struct
2051+
// (i.e. uses whatever bits were lying around as an initial value).
2052+
2053+
// FIXME: It would be nice to instead import the declaration but mark
2054+
// it as unavailable, but then it might get used as a type for an
2055+
// imported function and the developer would be able to use it without
2056+
// referencing the name, which would sidestep our availability
2057+
// diagnostics.
2058+
Impl.addImportDiagnostic(
2059+
decl,
2060+
Diagnostic(
2061+
diag::record_non_trivial_copy_destroy,
2062+
Impl.SwiftContext.AllocateCopy(decl->getNameAsString())),
2063+
decl->getLocation());
2064+
return nullptr;
2065+
}
20472066
}
20482067

20492068
// Import the name.
@@ -2346,8 +2365,12 @@ namespace {
23462365
}
23472366
}
23482367

2349-
if (auto structResult = dyn_cast<StructDecl>(result))
2368+
if (auto structResult = dyn_cast<StructDecl>(result)) {
23502369
structResult->setHasUnreferenceableStorage(hasUnreferenceableStorage);
2370+
if (isNonTrivialPtrAuth) {
2371+
structResult->setHasNonTrivialPtrAuth(true);
2372+
}
2373+
}
23512374

23522375
if (cxxRecordDecl) {
23532376
if (auto structResult = dyn_cast<StructDecl>(result))

lib/IRGen/Callee.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ namespace irgen {
7878
llvm::Value *storageAddress,
7979
const PointerAuthEntity &entity);
8080

81-
static PointerAuthInfo emit(IRGenModule &IGM,
82-
clang::PointerAuthQualifier pointerAuthQual);
81+
static PointerAuthInfo emit(IRGenFunction &IGF,
82+
clang::PointerAuthQualifier pointerAuthQual,
83+
llvm::Value *storageAddress);
8384

8485
static PointerAuthInfo forFunctionPointer(IRGenModule &IGM,
8586
CanSILFunctionType fnType);

lib/IRGen/GenPointerAuth.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,30 @@ PointerAuthInfo PointerAuthInfo::emit(IRGenFunction &IGF,
239239
}
240240

241241
PointerAuthInfo
242-
PointerAuthInfo::emit(IRGenModule &IGM,
243-
clang::PointerAuthQualifier pointerAuthQual) {
244-
return PointerAuthInfo(
245-
pointerAuthQual.getKey(),
246-
llvm::ConstantInt::get(IGM.Int64Ty,
247-
pointerAuthQual.getExtraDiscriminator()));
242+
PointerAuthInfo::emit(IRGenFunction &IGF,
243+
clang::PointerAuthQualifier pointerAuthQual,
244+
llvm::Value *storageAddress) {
245+
unsigned key = pointerAuthQual.getKey();
246+
247+
// Produce the 'other' discriminator.
248+
auto otherDiscriminator = pointerAuthQual.getExtraDiscriminator();
249+
llvm::Value *discriminator =
250+
llvm::ConstantInt::get(IGF.IGM.Int64Ty, otherDiscriminator);
251+
252+
// Factor in the address.
253+
if (pointerAuthQual.isAddressDiscriminated()) {
254+
assert(storageAddress &&
255+
"no storage address for address-discriminated schema");
256+
257+
if (otherDiscriminator != 0) {
258+
discriminator = emitPointerAuthBlend(IGF, storageAddress, discriminator);
259+
} else {
260+
discriminator =
261+
IGF.Builder.CreatePtrToInt(storageAddress, IGF.IGM.Int64Ty);
262+
}
263+
}
264+
265+
return PointerAuthInfo(key, discriminator);
248266
}
249267

250268
llvm::ConstantInt *

lib/IRGen/GenStruct.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,8 @@ class ClangRecordLowering {
10451045
return AddressOnlyCXXClangRecordTypeInfo::create(
10461046
FieldInfos, llvmType, TotalStride, TotalAlignment, ClangDecl);
10471047
}
1048+
// TODO: New AddressOnlyPtrAuthTypeInfo to handle address diversified field
1049+
// function ptrs in C structs.
10481050
return LoadableClangRecordTypeInfo::create(
10491051
FieldInfos, NextExplosionIndex, llvmType, TotalStride,
10501052
std::move(SpareBits), TotalAlignment, ClangDecl);

lib/IRGen/IRGenSIL.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5811,7 +5811,8 @@ void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) {
58115811
auto *signedFptr = Builder.CreateLoad(pointerToIntPtr, Int64PtrTy,
58125812
IGM.getPointerAlignment());
58135813
auto *resignedFptr = emitPointerAuthResign(
5814-
*this, signedFptr, PointerAuthInfo::emit(IGM, pointerAuthQual),
5814+
*this, signedFptr,
5815+
PointerAuthInfo::emit(*this, pointerAuthQual, pointerToSignedFptr),
58155816
PointerAuthInfo::emit(*this,
58165817
IGM.getOptions().PointerAuth.FunctionPointers,
58175818
pointerToSignedFptr, PointerAuthEntity()));
@@ -5926,6 +5927,8 @@ void IRGenSILFunction::visitEndAccessInst(EndAccessInst *i) {
59265927
auto pointerAuthQual = cast<StructElementAddrInst>(access->getOperand())
59275928
->getField()
59285929
->getPointerAuthQualifier();
5930+
auto *pointerToSignedFptr =
5931+
getLoweredAddress(access->getOperand()).getAddress();
59295932
auto tempAddress = getLoweredAddress(access);
59305933
auto *tempAddressToIntPtr =
59315934
Builder.CreateBitCast(tempAddress.getAddress(), Int64PtrPtrTy);
@@ -5936,10 +5939,8 @@ void IRGenSILFunction::visitEndAccessInst(EndAccessInst *i) {
59365939
PointerAuthInfo::emit(*this,
59375940
IGM.getOptions().PointerAuth.FunctionPointers,
59385941
tempAddress.getAddress(), PointerAuthEntity()),
5939-
PointerAuthInfo::emit(IGM, pointerAuthQual));
5942+
PointerAuthInfo::emit(*this, pointerAuthQual, pointerToSignedFptr));
59405943

5941-
auto *pointerToSignedFptr =
5942-
getLoweredAddress(access->getOperand()).getAddress();
59435944
auto *pointerToIntPtr =
59445945
Builder.CreateBitCast(pointerToSignedFptr, Int64PtrPtrTy);
59455946
Builder.CreateStore(signedFptr, pointerToIntPtr, IGM.getPointerAlignment());

lib/SIL/IR/TypeLowering.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,6 +2271,10 @@ namespace {
22712271

22722272
// Classify the type according to its stored properties.
22732273
for (auto field : D->getStoredProperties()) {
2274+
auto pointerAuthQual = field->getPointerAuthQualifier();
2275+
if (pointerAuthQual && pointerAuthQual.isAddressDiscriminated()) {
2276+
properties.setAddressOnly();
2277+
}
22742278
auto substFieldType =
22752279
field->getInterfaceType().subst(subMap)
22762280
->getCanonicalType();

lib/SILGen/SILGenLValue.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ namespace {
854854
auto Res = SGF.B.createStructElementAddr(loc, base.getValue(),
855855
Field, SubstFieldType);
856856

857-
if (!Field->getPointerAuthQualifier() ||
857+
if (!Field->getPointerAuthQualifier().isPresent() ||
858858
!SGF.getOptions().EnableImportPtrauthFieldFunctionPointers) {
859859
return ManagedValue::forLValue(Res);
860860
}

test/IRGen/Inputs/ptrauth_field_fptr_import.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22
#define TEST_C_FUNCTION
33

44
int returnInt() { return 111; }
5+
56
struct SecureStruct {
6-
int (*__ptrauth(2, 0, 88)(secure_func_ptr))();
7+
int (*__ptrauth(1, 0, 88)(secure_func_ptr))();
8+
};
9+
10+
struct AddressDiscriminatedSecureStruct {
11+
int (*__ptrauth(1, 1, 88)(secure_func_ptr))();
712
};
813

914
struct SecureStruct *ptr_to_secure_struct;
15+
struct AddressDiscriminatedSecureStruct
16+
*ptr_to_addr_discriminated_secure_struct;
1017
#endif

test/IRGen/ptrauth_field_fptr_import.swift

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %swift-frontend %s -enable-import-ptrauth-field-function-pointers -emit-ir -target arm64e-apple-ios13.0 -I %S/Inputs/ -validate-tbd-against-ir=none 2>&1 | %FileCheck %s
2+
23
// REQUIRES: CPU=arm64e
34
// REQUIRES: OS=ios
45

@@ -16,7 +17,7 @@ import PointerAuth
1617
// CHECK: [[CAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64*
1718
// CHECK: [[PTR:%.*]] = load i64*, i64** [[CAST2]], align 8
1819
// CHECK: [[SIGNEDINT:%.*]] = ptrtoint i64* [[PTR]] to i64
19-
// CHECK: [[DEFAULTSIGNVAL:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[SIGNEDINT]], i32 2, i64 88, i32 0, i64 0)
20+
// CHECK: [[DEFAULTSIGNVAL:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[SIGNEDINT]], i32 1, i64 88, i32 0, i64 0)
2021
// CHECK: [[AUTHPTR:%.*]] = inttoptr i64 [[DEFAULTSIGNVAL]] to i64*
2122
// CHECK: [[TMPCAST1:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64**
2223
// CHECK: store i64* [[AUTHPTR]], i64** [[TMPCAST1]], align 8
@@ -37,13 +38,66 @@ func test_field_fn_read() -> Int32 {
3738
// CHECK: [[CAST3:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64**
3839
// CHECK: [[LD:%.*]] = load i64*, i64** [[CAST3]], align 8
3940
// CHECK: [[CAST4:%.*]] = ptrtoint i64* [[LD]] to i64
40-
// CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[CAST4]], i32 0, i64 0, i32 2, i64 88)
41+
// CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[CAST4]], i32 0, i64 0, i32 1, i64 88)
4142
// CHECK: [[CAST5:%.*]] = inttoptr i64 [[SIGN]] to i64*
4243
// CHECK: [[CAST6:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64**
4344
// CHECK: store i64* [[CAST5]], i64** [[CAST6]], align 8
4445
func test_field_fn_ptr_modify() {
4546
ptr_to_secure_struct!.pointee.secure_func_ptr = returnInt
4647
}
4748

49+
// CHECK: define hidden swiftcc i32 @"$s25ptrauth_field_fptr_import024test_addr_discriminated_B8_fn_reads5Int32VyF"() #0 {
50+
// CHECK: [[LD:%.*]] = load i64, i64* bitcast (%struct.AddressDiscriminatedSecureStruct** @ptr_to_addr_discriminated_secure_struct to i64*), align 8
51+
// CHECK: 5: ; preds = %entry
52+
// CHECK: [[CAST0:%.*]] = inttoptr i64 [[LD]] to i8*
53+
// CHECK: br label %12
54+
// CHECK: 12:
55+
// CHECK: [[AddressDiscriminatedSecureStruct:%.*]] = phi i8* [ [[CAST0]], %5 ]
56+
// CHECK: [[CAST1:%.*]] = bitcast i8* [[AddressDiscriminatedSecureStruct]] to %TSo32AddressDiscriminatedSecureStructV*
57+
// CHECK: %.secure_func_ptr = getelementptr inbounds %TSo32AddressDiscriminatedSecureStructV, %TSo32AddressDiscriminatedSecureStructV* %14, i32 0, i32 0
58+
// CHECK: [[CAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64**
59+
// CHECK: [[PTR:%.*]] = load i64*, i64** [[CAST2]], align 8
60+
// CHECK: [[ADDR:%.*]] = ptrtoint %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64
61+
// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ADDR]], i64 88)
62+
// CHECK: [[CAST3:%.*]] = ptrtoint i64* [[PTR]] to i64
63+
// CHECK: [[DEFAULTSIGNVAL:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[CAST3]], i32 1, i64 [[BLEND]], i32 0, i64 0)
64+
// CHECK: [[AUTHPTR:%.*]] = inttoptr i64 [[DEFAULTSIGNVAL]] to i64*
65+
// CHECK: [[TMPCAST1:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64**
66+
// CHECK: store i64* [[AUTHPTR]], i64** [[TMPCAST1]], align 8
67+
// CHECK: [[TMPCAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64*
68+
// CHECK: [[FUNCPTR:%.*]] = load i64, i64* [[TMPCAST2]], align 8
69+
func test_addr_discriminated_field_fn_read() -> Int32 {
70+
let fn = ptr_to_addr_discriminated_secure_struct!.pointee.secure_func_ptr!
71+
return fn()
72+
}
73+
74+
// CHECK-LABEL: define hidden swiftcc void @"$s25ptrauth_field_fptr_import024test_addr_discriminated_B14_fn_ptr_modifyyyF"() #0 {
75+
// CHECK: 11: ; preds = %4
76+
// CHECK: [[AddressDiscriminatedSecureStruct:%.*]] = phi i8* [ %5, %4 ]
77+
// CHECK: [[CAST1:%.*]] = bitcast i8* [[AddressDiscriminatedSecureStruct]] to %TSo32AddressDiscriminatedSecureStructV*
78+
// CHECK: %.secure_func_ptr = getelementptr inbounds %TSo32AddressDiscriminatedSecureStructV, %TSo32AddressDiscriminatedSecureStructV* [[CAST1]], i32 0, i32 0
79+
// CHECK: [[CAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64*
80+
// CHECK: store i64 ptrtoint ({ i8*, i32, i64, i64 }* @returnInt.ptrauth to i64), i64* [[CAST2]], align 8
81+
// CHECK: [[CAST3:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64**
82+
// CHECK: [[LD:%.*]] = load i64*, i64** [[CAST3]], align 8
83+
// CHECK: [[CAST4:%.*]] = ptrtoint %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64
84+
// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST4]], i64 88)
85+
// CHECK: [[CAST5:%.*]] = ptrtoint i64* [[LD]] to i64
86+
// CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[CAST5]], i32 0, i64 0, i32 1, i64 [[BLEND]])
87+
// CHECK: [[CAST5:%.*]] = inttoptr i64 [[SIGN]] to i64*
88+
// CHECK: [[CAST6:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64**
89+
// CHECK: store i64* [[CAST5]], i64** [[CAST6]], align 8
90+
func test_addr_discriminated_field_fn_ptr_modify() {
91+
ptr_to_addr_discriminated_secure_struct!.pointee.secure_func_ptr = returnInt
92+
}
93+
94+
// TODO: Unimplemented non trivial pointer auth copy function
95+
// func test_addr_discriminated_copy() -> Int32 {
96+
// let struct_with_signed_val = ptr_to_addr_discriminated_secure_struct.pointee
97+
// return struct_with_signed_val.secure_func_ptr()
98+
// }
99+
48100
print(test_field_fn_read())
49-
print(test_field_fn_ptr_modify())
101+
test_field_fn_ptr_modify()
102+
print(test_addr_discriminated_field_fn_read())
103+
test_addr_discriminated_field_fn_ptr_modify()

test/SILGen/Inputs/ptrauth_field_fptr_import.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22
#define TEST_C_FUNCTION
33

44
struct SecureStruct {
5-
int (*__ptrauth(2, 0, 88)(secure_func_ptr1))();
6-
int (*__ptrauth(3, 0, 66)(secure_func_ptr2))();
5+
int (*__ptrauth(1, 0, 88)(secure_func_ptr1))();
6+
int (*__ptrauth(1, 0, 66)(secure_func_ptr2))();
7+
};
8+
9+
struct AddressDiscriminatedSecureStruct {
10+
int (*__ptrauth(1, 1, 88)(secure_func_ptr1))();
11+
int (*__ptrauth(1, 1, 66)(secure_func_ptr2))();
712
};
813

914
struct SecureStruct *ptr_to_secure_struct;
15+
struct AddressDiscriminatedSecureStruct
16+
*ptr_to_addr_discriminated_secure_struct;
1017
#endif

0 commit comments

Comments
 (0)