Skip to content

Commit 01154c6

Browse files
committed
Add IRGen support for reads and writes to imported __ptrauth qualified field function pointers
Access to such pointers are protected by begin_access [signed]/end_access. Generate code to auth/sign them before access.
1 parent 2582810 commit 01154c6

File tree

6 files changed

+139
-2
lines changed

6 files changed

+139
-2
lines changed

lib/IRGen/Callee.h

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

81+
static PointerAuthInfo emit(IRGenModule &IGM,
82+
clang::PointerAuthQualifier pointerAuthQual);
83+
8184
static PointerAuthInfo forFunctionPointer(IRGenModule &IGM,
8285
CanSILFunctionType fnType);
8386

lib/IRGen/GenPointerAuth.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,15 @@ PointerAuthInfo PointerAuthInfo::emit(IRGenFunction &IGF,
238238
return PointerAuthInfo(key, discriminator);
239239
}
240240

241+
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()));
248+
}
249+
241250
llvm::ConstantInt *
242251
PointerAuthInfo::getOtherDiscriminator(IRGenModule &IGM,
243252
const PointerAuthSchema &schema,

lib/IRGen/IRGenSIL.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5750,7 +5750,6 @@ void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) {
57505750

57515751
case SILAccessEnforcement::Static:
57525752
case SILAccessEnforcement::Unsafe:
5753-
case SILAccessEnforcement::Signed:
57545753
// nothing to do
57555754
setLoweredAddress(access, addr);
57565755
return;
@@ -5773,6 +5772,41 @@ void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) {
57735772
setLoweredDynamicallyEnforcedAddress(access, addr, scratch);
57745773
return;
57755774
}
5775+
case SILAccessEnforcement::Signed: {
5776+
auto &ti = getTypeInfo(access->getType());
5777+
auto *sea = cast<StructElementAddrInst>(access->getOperand());
5778+
auto *Int64PtrTy = llvm::Type::getInt64PtrTy(IGM.getLLVMContext());
5779+
auto *Int64PtrPtrTy = Int64PtrTy->getPointerTo();
5780+
if (access->getAccessKind() == SILAccessKind::Read) {
5781+
// When we see a signed read access, generate code to:
5782+
// authenticate the signed pointer, and store the authenticated value to a
5783+
// shadow stack location. Set the lowered address of the access to this
5784+
// stack location.
5785+
auto pointerAuthQual = sea->getField()->getPointerAuthQualifier();
5786+
auto *pointerToSignedFptr = getLoweredAddress(sea).getAddress();
5787+
auto *pointerToIntPtr =
5788+
Builder.CreateBitCast(pointerToSignedFptr, Int64PtrPtrTy);
5789+
auto *signedFptr = Builder.CreateLoad(pointerToIntPtr, Int64PtrTy,
5790+
IGM.getPointerAlignment());
5791+
auto *unsignedFptr = emitPointerAuthAuth(
5792+
*this, signedFptr, PointerAuthInfo::emit(IGM, pointerAuthQual));
5793+
auto temp = ti.allocateStack(*this, access->getType(), "ptrauth.temp");
5794+
auto *tempAddressToIntPtr =
5795+
Builder.CreateBitCast(temp.getAddressPointer(), Int64PtrPtrTy);
5796+
Builder.CreateStore(unsignedFptr, tempAddressToIntPtr,
5797+
IGM.getPointerAlignment());
5798+
setLoweredAddress(access, temp.getAddress());
5799+
return;
5800+
}
5801+
if (access->getAccessKind() == SILAccessKind::Modify) {
5802+
// When we see a signed modify access, create a shadow stack location and
5803+
// set the lowered address of the access to this stack location.
5804+
auto temp = ti.allocateStack(*this, access->getType(), "ptrauth.temp");
5805+
setLoweredAddress(access, temp.getAddress());
5806+
return;
5807+
}
5808+
llvm_unreachable("Incompatible access kind with begin_access [signed]");
5809+
}
57765810
}
57775811
llvm_unreachable("bad access enforcement");
57785812
}
@@ -5837,7 +5871,6 @@ void IRGenSILFunction::visitEndAccessInst(EndAccessInst *i) {
58375871

58385872
case SILAccessEnforcement::Static:
58395873
case SILAccessEnforcement::Unsafe:
5840-
case SILAccessEnforcement::Signed:
58415874
// nothing to do
58425875
return;
58435876

@@ -5854,6 +5887,35 @@ void IRGenSILFunction::visitEndAccessInst(EndAccessInst *i) {
58545887
Builder.CreateLifetimeEnd(scratch);
58555888
return;
58565889
}
5890+
5891+
case SILAccessEnforcement::Signed: {
5892+
if (access->getAccessKind() != SILAccessKind::Modify) {
5893+
// nothing to do.
5894+
return;
5895+
}
5896+
// When we see a signed modify access, get the lowered address of the
5897+
// access which is the shadow stack slot, sign the value and write back to
5898+
// the struct field.
5899+
auto *Int64PtrTy = llvm::Type::getInt64PtrTy(IGM.getLLVMContext());
5900+
auto *Int64PtrPtrTy = Int64PtrTy->getPointerTo();
5901+
auto pointerAuthQual = cast<StructElementAddrInst>(access->getOperand())
5902+
->getField()
5903+
->getPointerAuthQualifier();
5904+
auto tempAddress = getLoweredAddress(access);
5905+
auto *tempAddressToIntPtr =
5906+
Builder.CreateBitCast(tempAddress.getAddress(), Int64PtrPtrTy);
5907+
auto *tempAddressValue = Builder.CreateLoad(tempAddressToIntPtr, Int64PtrTy,
5908+
IGM.getPointerAlignment());
5909+
auto *signedFptr = emitPointerAuthSign(
5910+
*this, tempAddressValue, PointerAuthInfo::emit(IGM, pointerAuthQual));
5911+
5912+
auto *pointerToSignedFptr =
5913+
getLoweredAddress(access->getOperand()).getAddress();
5914+
auto *pointerToIntPtr =
5915+
Builder.CreateBitCast(pointerToSignedFptr, Int64PtrPtrTy);
5916+
Builder.CreateStore(signedFptr, pointerToIntPtr, IGM.getPointerAlignment());
5917+
return;
5918+
}
58575919
}
58585920
llvm_unreachable("bad access enforcement");
58595921
}

test/IRGen/Inputs/module.modulemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@ module SynthesizedProtocol {
3131
header "synthesized_protocol.h"
3232
export *
3333
}
34+
35+
module PointerAuth {
36+
header "ptrauth_field_fptr_import.h"
37+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef TEST_C_FUNCTION
2+
#define TEST_C_FUNCTION
3+
4+
int returnInt() {
5+
return 111;
6+
}
7+
struct SecureStruct {
8+
int (* __ptrauth(2, 0, 88)(secure_func_ptr))();
9+
};
10+
11+
struct SecureStruct* ptr_to_secure_struct;
12+
#endif
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// 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+
3+
import PointerAuth
4+
5+
// CHECK: define hidden swiftcc i32 @"$s25ptrauth_field_fptr_import05test_B8_fn_reads5Int32VyF"() #0 {
6+
// CHECK: [[LD:%.*]] = load i64, i64* bitcast (%struct.SecureStruct** @ptr_to_secure_struct to i64*), align 8
7+
// CHECK: 5: ; preds = %entry
8+
// CHECK: [[CAST0:%.*]] = inttoptr i64 [[LD]] to i8*
9+
// CHECK: br label %12
10+
// CHECK: 12:
11+
// CHECK: [[SECURESTRUCT:%.*]] = phi i8* [ [[CAST0]], %5 ]
12+
// CHECK: [[CAST1:%.*]] = bitcast i8* [[SECURESTRUCT]] to %TSo12SecureStructV*
13+
// CHECK: %.secure_func_ptr = getelementptr inbounds %TSo12SecureStructV, %TSo12SecureStructV* %14, i32 0, i32 0
14+
// CHECK: [[CAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64*
15+
// CHECK: [[PTR:%.*]] = load i64*, i64** [[CAST2]], align 8
16+
// CHECK: [[SIGNEDINT:%.*]] = ptrtoint i64* [[PTR]] to i64
17+
// CHECK: [[AUTHVAL:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNEDINT]], i32 2, i64 88)
18+
// CHECK: [[AUTHPTR:%.*]] = inttoptr i64 [[AUTHVAL]] to i64*
19+
// CHECK: [[TMPCAST1:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64**
20+
// CHECK: store i64* [[AUTHPTR]], i64** [[TMPCAST1]], align 8
21+
// CHECK: [[TMPCAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64*
22+
// CHECK: [[FUNCPTR:%.*]] = load i64, i64* [[TMPCAST2]], align 8
23+
func test_field_fn_read() -> Int32 {
24+
let fn = ptr_to_secure_struct!.pointee.secure_func_ptr!
25+
return fn()
26+
}
27+
28+
// CHECK-LABEL: define hidden swiftcc void @"$s25ptrauth_field_fptr_import05test_B14_fn_ptr_modifyyyF"() #0 {
29+
// CHECK: 11: ; preds = %4
30+
// CHECK: [[SECURESTRUCT:%.*]] = phi i8* [ %5, %4 ]
31+
// CHECK: [[CAST1:%.*]] = bitcast i8* [[SECURESTRUCT]] to %TSo12SecureStructV*
32+
// CHECK: %.secure_func_ptr = getelementptr inbounds %TSo12SecureStructV, %TSo12SecureStructV* [[CAST1]], i32 0, i32 0
33+
// CHECK: [[CAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64*
34+
// CHECK: store i64 ptrtoint ({ i8*, i32, i64, i64 }* @returnInt.ptrauth to i64), i64* [[CAST2]], align 8
35+
// CHECK: [[CAST3:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64**
36+
// CHECK: [[LD:%.*]] = load i64*, i64** [[CAST3]], align 8
37+
// CHECK: [[CAST4:%.*]] = ptrtoint i64* [[LD]] to i64
38+
// CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CAST4]], i32 2, i64 88)
39+
// CHECK: [[CAST5:%.*]] = inttoptr i64 [[SIGN]] to i64*
40+
// CHECK: [[CAST6:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64**
41+
// CHECK: store i64* [[CAST5]], i64** [[CAST6]], align 8
42+
func test_field_fn_ptr_modify() {
43+
ptr_to_secure_struct!.pointee.secure_func_ptr = returnInt
44+
}
45+
46+
print(test_field_fn_read())
47+
print(test_field_fn_ptr_modify())

0 commit comments

Comments
 (0)