Skip to content

Commit a286d14

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 4d4f003 commit a286d14

File tree

7 files changed

+145
-3
lines changed

7 files changed

+145
-3
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/GenCall.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5035,7 +5035,7 @@ Callee irgen::getCFunctionPointerCallee(IRGenFunction &IGF,
50355035
CalleeInfo &&calleeInfo) {
50365036
auto sig = emitCastOfFunctionPointer(IGF, fnPtr, calleeInfo.OrigFnType);
50375037
auto authInfo =
5038-
PointerAuthInfo::forFunctionPointer(IGF.IGM, calleeInfo.OrigFnType);
5038+
PointerAuthInfo::forFunctionPointer(IGF.IGM, calleeInfo.OrigFnType);
50395039

50405040
auto fn = FunctionPointer::createSigned(FunctionPointer::Kind::Function,
50415041
fnPtr, authInfo, sig);

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: 71 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,44 @@ 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 *resignedFptr = emitPointerAuthResign(
5792+
*this, signedFptr, PointerAuthInfo::emit(IGM, pointerAuthQual),
5793+
PointerAuthInfo::emit(*this,
5794+
IGM.getOptions().PointerAuth.FunctionPointers,
5795+
pointerToSignedFptr, PointerAuthEntity()));
5796+
auto temp = ti.allocateStack(*this, access->getType(), "ptrauth.temp");
5797+
auto *tempAddressToIntPtr =
5798+
Builder.CreateBitCast(temp.getAddressPointer(), Int64PtrPtrTy);
5799+
Builder.CreateStore(resignedFptr, tempAddressToIntPtr,
5800+
IGM.getPointerAlignment());
5801+
setLoweredAddress(access, temp.getAddress());
5802+
return;
5803+
}
5804+
if (access->getAccessKind() == SILAccessKind::Modify) {
5805+
// When we see a signed modify access, create a shadow stack location and
5806+
// set the lowered address of the access to this stack location.
5807+
auto temp = ti.allocateStack(*this, access->getType(), "ptrauth.temp");
5808+
setLoweredAddress(access, temp.getAddress());
5809+
return;
5810+
}
5811+
llvm_unreachable("Incompatible access kind with begin_access [signed]");
5812+
}
57765813
}
57775814
llvm_unreachable("bad access enforcement");
57785815
}
@@ -5837,7 +5874,6 @@ void IRGenSILFunction::visitEndAccessInst(EndAccessInst *i) {
58375874

58385875
case SILAccessEnforcement::Static:
58395876
case SILAccessEnforcement::Unsafe:
5840-
case SILAccessEnforcement::Signed:
58415877
// nothing to do
58425878
return;
58435879

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

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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef TEST_C_FUNCTION
2+
#define TEST_C_FUNCTION
3+
4+
int returnInt() { return 111; }
5+
struct SecureStruct {
6+
int (*__ptrauth(2, 0, 88)(secure_func_ptr))();
7+
};
8+
9+
struct SecureStruct *ptr_to_secure_struct;
10+
#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: [[DEFAULTSIGNVAL:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[SIGNEDINT]], i32 2, i64 88, i32 0, i64 0)
18+
// CHECK: [[AUTHPTR:%.*]] = inttoptr i64 [[DEFAULTSIGNVAL]] 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.resign(i64 [[CAST4]], i32 0, i64 0, 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)