Skip to content

Initial support for ptrauth qualified function pointers in C #63204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4522,6 +4522,7 @@ begin_access
sil-enforcement ::= static
sil-enforcement ::= dynamic
sil-enforcement ::= unsafe
sil-enforcement ::= signed
%1 = begin_access [read] [unknown] %0 : $*T
// %0 must be of $*T type.

Expand Down Expand Up @@ -4564,6 +4565,9 @@ runtime for the duration of its scope. This access may still conflict with an
outer access scope; therefore may still require dynamic enforcement at a single
point.

A ``signed`` access is for pointers that are signed in architectures that support
pointer signing.

A ``builtin`` access was emitted for a user-controlled Builtin (e.g. the
standard library's KeyPath access). Non-builtin accesses are auto-generated by
the compiler to enforce formal access that derives from the language. A
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5939,6 +5939,8 @@ class VarDecl : public AbstractStorageDecl {
});
}

clang::PointerAuthQualifier getPointerAuthQualifier() const;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() == DeclKind::Var || D->getKind() == DeclKind::Param;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/SILOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ class SILOptions {
/// temporaries for stack protection.
bool EnableMoveInoutStackProtection = false;

/// Enables codegen support for clang imported ptrauth qualified field
/// function pointers.
bool EnableImportPtrauthFieldFunctionPointers = false;

/// Controls whether or not paranoid verification checks are run.
bool VerifyAll = false;

Expand Down
7 changes: 7 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,13 @@ def enable_move_inout_stack_protector :
Flag<["-"], "enable-move-inout-stack-protector">,
HelpText<"Enable the stack protector by moving values to temporaries">;

def enable_import_ptrauth_field_function_pointers :
Flag<["-"], "enable-import-ptrauth-field-function-pointers">,
HelpText<"Enable import of custom ptrauth qualified field function pointers">;
def disable_import_ptrauth_field_function_pointers :
Flag<["-"], "disable-import-ptrauth-field-function-pointers">,
HelpText<"Disable import of custom ptrauth qualified field function pointers">;

def enable_collocate_metadata_functions :
Flag<["-"], "enable-collocate-metadata-functions">,
HelpText<"Enable collocate metadata functions">;
Expand Down
11 changes: 8 additions & 3 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -4418,8 +4418,13 @@ enum class SILAccessEnforcement : uint8_t {
/// behavior.
Unsafe,

/// Access to pointers that are signed via pointer authentication mechanishm.
/// Such pointers should be authenticated before read and signed before a
/// write. Optimizer should avoid promoting such accesses to values.
Signed,

// This enum is encoded.
Last = Unsafe
Last = Signed
};
StringRef getSILAccessEnforcementName(SILAccessEnforcement enforcement);

Expand Down Expand Up @@ -4492,9 +4497,9 @@ class BeginAccessInst
: BeginAccessBase(loc, accessKind, enforcement, noNestedConflict,
fromBuiltin, lvalue, lvalue->getType()) {

static_assert(unsigned(SILAccessKind::Last) < (1 << 2),
static_assert(unsigned(SILAccessKind::Last) < (1 << 3),
"reserve sufficient bits for serialized SIL");
static_assert(unsigned(SILAccessEnforcement::Last) < (1 << 2),
static_assert(unsigned(SILAccessEnforcement::Last) < (1 << 3),
"reserve sufficient bits for serialized SIL");

static_assert(unsigned(SILAccessKind::Last) <
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class alignas(8) SILNode :
enum { NumAssignOwnershipQualifierBits = 2 };
enum { NumAssignByWrapperModeBits = 2 };
enum { NumSILAccessKindBits = 2 };
enum { NumSILAccessEnforcementBits = 2 };
enum { NumSILAccessEnforcementBits = 3 };
enum { NumAllocRefTailTypesBits = 4 };

protected:
Expand Down
9 changes: 9 additions & 0 deletions lib/AST/TypeWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@ bool VarDecl::isTypeWrapperLocalStorageForInitializer() const {
}
return false;
}

clang::PointerAuthQualifier VarDecl::getPointerAuthQualifier() const {
if (auto *clangDecl = getClangDecl()) {
if (auto *valueDecl = dyn_cast<clang::ValueDecl>(clangDecl)) {
return valueDecl->getType().getPointerAuth();
}
}
return clang::PointerAuthQualifier();
}
10 changes: 7 additions & 3 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1823,9 +1823,13 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
Opts.EnableStackProtection =
Args.hasFlag(OPT_enable_stack_protector, OPT_disable_stack_protector,
Opts.EnableStackProtection);
Opts.EnableMoveInoutStackProtection =
Args.hasFlag(OPT_enable_move_inout_stack_protector, OPT_disable_stack_protector,
Opts.EnableMoveInoutStackProtection);
Opts.EnableMoveInoutStackProtection = Args.hasArg(
OPT_enable_move_inout_stack_protector, OPT_disable_stack_protector,
Opts.EnableMoveInoutStackProtection);
Opts.EnableImportPtrauthFieldFunctionPointers =
Args.hasArg(OPT_enable_import_ptrauth_field_function_pointers,
OPT_disable_import_ptrauth_field_function_pointers,
Opts.EnableImportPtrauthFieldFunctionPointers);
Opts.VerifyAll |= Args.hasArg(OPT_sil_verify_all);
Opts.VerifyNone |= Args.hasArg(OPT_sil_verify_none);
Opts.DebugSerialization |= Args.hasArg(OPT_sil_debug_serialization);
Expand Down
3 changes: 3 additions & 0 deletions lib/IRGen/Callee.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ namespace irgen {
llvm::Value *storageAddress,
const PointerAuthEntity &entity);

static PointerAuthInfo emit(IRGenModule &IGM,
clang::PointerAuthQualifier pointerAuthQual);

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

Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5035,7 +5035,7 @@ Callee irgen::getCFunctionPointerCallee(IRGenFunction &IGF,
CalleeInfo &&calleeInfo) {
auto sig = emitCastOfFunctionPointer(IGF, fnPtr, calleeInfo.OrigFnType);
auto authInfo =
PointerAuthInfo::forFunctionPointer(IGF.IGM, calleeInfo.OrigFnType);
PointerAuthInfo::forFunctionPointer(IGF.IGM, calleeInfo.OrigFnType);

auto fn = FunctionPointer::createSigned(FunctionPointer::Kind::Function,
fnPtr, authInfo, sig);
Expand Down
9 changes: 9 additions & 0 deletions lib/IRGen/GenPointerAuth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,15 @@ PointerAuthInfo PointerAuthInfo::emit(IRGenFunction &IGF,
return PointerAuthInfo(key, discriminator);
}

PointerAuthInfo
PointerAuthInfo::emit(IRGenModule &IGM,
clang::PointerAuthQualifier pointerAuthQual) {
return PointerAuthInfo(
pointerAuthQual.getKey(),
llvm::ConstantInt::get(IGM.Int64Ty,
pointerAuthQual.getExtraDiscriminator()));
}

llvm::ConstantInt *
PointerAuthInfo::getOtherDiscriminator(IRGenModule &IGM,
const PointerAuthSchema &schema,
Expand Down
73 changes: 73 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5772,6 +5772,44 @@ void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) {
setLoweredDynamicallyEnforcedAddress(access, addr, scratch);
return;
}
case SILAccessEnforcement::Signed: {
auto &ti = getTypeInfo(access->getType());
auto *sea = cast<StructElementAddrInst>(access->getOperand());
auto *Int64PtrTy = llvm::Type::getInt64PtrTy(IGM.getLLVMContext());
auto *Int64PtrPtrTy = Int64PtrTy->getPointerTo();
if (access->getAccessKind() == SILAccessKind::Read) {
// When we see a signed read access, generate code to:
// authenticate the signed pointer, and store the authenticated value to a
// shadow stack location. Set the lowered address of the access to this
// stack location.
auto pointerAuthQual = sea->getField()->getPointerAuthQualifier();
auto *pointerToSignedFptr = getLoweredAddress(sea).getAddress();
auto *pointerToIntPtr =
Builder.CreateBitCast(pointerToSignedFptr, Int64PtrPtrTy);
auto *signedFptr = Builder.CreateLoad(pointerToIntPtr, Int64PtrTy,
IGM.getPointerAlignment());
auto *resignedFptr = emitPointerAuthResign(
*this, signedFptr, PointerAuthInfo::emit(IGM, pointerAuthQual),
PointerAuthInfo::emit(*this,
IGM.getOptions().PointerAuth.FunctionPointers,
pointerToSignedFptr, PointerAuthEntity()));
auto temp = ti.allocateStack(*this, access->getType(), "ptrauth.temp");
auto *tempAddressToIntPtr =
Builder.CreateBitCast(temp.getAddressPointer(), Int64PtrPtrTy);
Builder.CreateStore(resignedFptr, tempAddressToIntPtr,
IGM.getPointerAlignment());
setLoweredAddress(access, temp.getAddress());
return;
}
if (access->getAccessKind() == SILAccessKind::Modify) {
// When we see a signed modify access, create a shadow stack location and
// set the lowered address of the access to this stack location.
auto temp = ti.allocateStack(*this, access->getType(), "ptrauth.temp");
setLoweredAddress(access, temp.getAddress());
return;
}
llvm_unreachable("Incompatible access kind with begin_access [signed]");
}
}
llvm_unreachable("bad access enforcement");
}
Expand All @@ -5790,6 +5828,7 @@ void IRGenSILFunction::visitBeginUnpairedAccessInst(

case SILAccessEnforcement::Static:
case SILAccessEnforcement::Unsafe:
case SILAccessEnforcement::Signed:
// nothing to do
return;

Expand Down Expand Up @@ -5851,6 +5890,39 @@ void IRGenSILFunction::visitEndAccessInst(EndAccessInst *i) {
Builder.CreateLifetimeEnd(scratch);
return;
}

case SILAccessEnforcement::Signed: {
if (access->getAccessKind() != SILAccessKind::Modify) {
// nothing to do.
return;
}
// When we see a signed modify access, get the lowered address of the
// access which is the shadow stack slot, sign the value and write back to
// the struct field.
auto *Int64PtrTy = llvm::Type::getInt64PtrTy(IGM.getLLVMContext());
auto *Int64PtrPtrTy = Int64PtrTy->getPointerTo();
auto pointerAuthQual = cast<StructElementAddrInst>(access->getOperand())
->getField()
->getPointerAuthQualifier();
auto tempAddress = getLoweredAddress(access);
auto *tempAddressToIntPtr =
Builder.CreateBitCast(tempAddress.getAddress(), Int64PtrPtrTy);
auto *tempAddressValue = Builder.CreateLoad(tempAddressToIntPtr, Int64PtrTy,
IGM.getPointerAlignment());
auto *signedFptr = emitPointerAuthResign(
*this, tempAddressValue,
PointerAuthInfo::emit(*this,
IGM.getOptions().PointerAuth.FunctionPointers,
tempAddress.getAddress(), PointerAuthEntity()),
PointerAuthInfo::emit(IGM, pointerAuthQual));

auto *pointerToSignedFptr =
getLoweredAddress(access->getOperand()).getAddress();
auto *pointerToIntPtr =
Builder.CreateBitCast(pointerToSignedFptr, Int64PtrPtrTy);
Builder.CreateStore(signedFptr, pointerToIntPtr, IGM.getPointerAlignment());
return;
}
}
llvm_unreachable("bad access enforcement");
}
Expand All @@ -5862,6 +5934,7 @@ void IRGenSILFunction::visitEndUnpairedAccessInst(EndUnpairedAccessInst *i) {

case SILAccessEnforcement::Static:
case SILAccessEnforcement::Unsafe:
case SILAccessEnforcement::Signed:
// nothing to do
return;

Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/IR/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,8 @@ StringRef swift::getSILAccessEnforcementName(SILAccessEnforcement enforcement) {
case SILAccessEnforcement::Static: return "static";
case SILAccessEnforcement::Dynamic: return "dynamic";
case SILAccessEnforcement::Unsafe: return "unsafe";
case SILAccessEnforcement::Signed:
return "signed";
}
llvm_unreachable("bad access enforcement");
}
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/Parser/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4326,6 +4326,8 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
setEnforcement(SILAccessEnforcement::Dynamic);
} else if (attr == "unsafe") {
setEnforcement(SILAccessEnforcement::Unsafe);
} else if (attr == "signed") {
setEnforcement(SILAccessEnforcement::Signed);
} else if (attr == "init") {
setKind(SILAccessKind::Init);
} else if (attr == "read") {
Expand Down
11 changes: 10 additions & 1 deletion lib/SILGen/SILGenLValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -853,8 +853,17 @@ namespace {
// TODO: if the base is +1, break apart its cleanup.
auto Res = SGF.B.createStructElementAddr(loc, base.getValue(),
Field, SubstFieldType);
return ManagedValue::forLValue(Res);

if (!Field->getPointerAuthQualifier() ||
!SGF.getOptions().EnableImportPtrauthFieldFunctionPointers) {
return ManagedValue::forLValue(Res);
}
auto beginAccess =
enterAccessScope(SGF, loc, base, Res, getTypeData(), getAccessKind(),
SILAccessEnforcement::Signed, takeActorIsolation());
return ManagedValue::forLValue(beginAccess);
}

void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "StructElementComponent("
<< Field->getName() << ")\n";
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ bool AccessMarkerElimination::shouldPreserveAccess(
switch (enforcement) {
case SILAccessEnforcement::Static:
case SILAccessEnforcement::Unsafe:
case SILAccessEnforcement::Signed:
return false;
case SILAccessEnforcement::Unknown:
case SILAccessEnforcement::Dynamic:
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/Utils/SILInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,7 @@ static InlineCost getEnforcementCost(SILAccessEnforcement enforcement) {
return InlineCost::Expensive;
case SILAccessEnforcement::Static:
case SILAccessEnforcement::Unsafe:
case SILAccessEnforcement::Signed:
return InlineCost::Free;
}
llvm_unreachable("bad enforcement");
Expand Down
4 changes: 4 additions & 0 deletions test/IRGen/Inputs/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ module SynthesizedProtocol {
header "synthesized_protocol.h"
export *
}

module PointerAuth {
header "ptrauth_field_fptr_import.h"
}
10 changes: 10 additions & 0 deletions test/IRGen/Inputs/ptrauth_field_fptr_import.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef TEST_C_FUNCTION
#define TEST_C_FUNCTION

int returnInt() { return 111; }
struct SecureStruct {
int (*__ptrauth(2, 0, 88)(secure_func_ptr))();
};

struct SecureStruct *ptr_to_secure_struct;
#endif
49 changes: 49 additions & 0 deletions test/IRGen/ptrauth_field_fptr_import.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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
// REQUIRES: CPU=arm64e
// REQUIRES: OS=ios

import PointerAuth

// CHECK: define hidden swiftcc i32 @"$s25ptrauth_field_fptr_import05test_B8_fn_reads5Int32VyF"() #0 {
// CHECK: [[LD:%.*]] = load i64, i64* bitcast (%struct.SecureStruct** @ptr_to_secure_struct to i64*), align 8
// CHECK: 5: ; preds = %entry
// CHECK: [[CAST0:%.*]] = inttoptr i64 [[LD]] to i8*
// CHECK: br label %12
// CHECK: 12:
// CHECK: [[SECURESTRUCT:%.*]] = phi i8* [ [[CAST0]], %5 ]
// CHECK: [[CAST1:%.*]] = bitcast i8* [[SECURESTRUCT]] to %TSo12SecureStructV*
// CHECK: %.secure_func_ptr = getelementptr inbounds %TSo12SecureStructV, %TSo12SecureStructV* %14, i32 0, i32 0
// CHECK: [[CAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64*
// CHECK: [[PTR:%.*]] = load i64*, i64** [[CAST2]], align 8
// CHECK: [[SIGNEDINT:%.*]] = ptrtoint i64* [[PTR]] to i64
// CHECK: [[DEFAULTSIGNVAL:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[SIGNEDINT]], i32 2, i64 88, i32 0, i64 0)
// CHECK: [[AUTHPTR:%.*]] = inttoptr i64 [[DEFAULTSIGNVAL]] to i64*
// CHECK: [[TMPCAST1:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64**
// CHECK: store i64* [[AUTHPTR]], i64** [[TMPCAST1]], align 8
// CHECK: [[TMPCAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64*
// CHECK: [[FUNCPTR:%.*]] = load i64, i64* [[TMPCAST2]], align 8
func test_field_fn_read() -> Int32 {
let fn = ptr_to_secure_struct!.pointee.secure_func_ptr!
return fn()
}

// CHECK-LABEL: define hidden swiftcc void @"$s25ptrauth_field_fptr_import05test_B14_fn_ptr_modifyyyF"() #0 {
// CHECK: 11: ; preds = %4
// CHECK: [[SECURESTRUCT:%.*]] = phi i8* [ %5, %4 ]
// CHECK: [[CAST1:%.*]] = bitcast i8* [[SECURESTRUCT]] to %TSo12SecureStructV*
// CHECK: %.secure_func_ptr = getelementptr inbounds %TSo12SecureStructV, %TSo12SecureStructV* [[CAST1]], i32 0, i32 0
// CHECK: [[CAST2:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64*
// CHECK: store i64 ptrtoint ({ i8*, i32, i64, i64 }* @returnInt.ptrauth to i64), i64* [[CAST2]], align 8
// CHECK: [[CAST3:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %ptrauth.temp to i64**
// CHECK: [[LD:%.*]] = load i64*, i64** [[CAST3]], align 8
// CHECK: [[CAST4:%.*]] = ptrtoint i64* [[LD]] to i64
// CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[CAST4]], i32 0, i64 0, i32 2, i64 88)
// CHECK: [[CAST5:%.*]] = inttoptr i64 [[SIGN]] to i64*
// CHECK: [[CAST6:%.*]] = bitcast %Ts5Int32VIetCd_Sg* %.secure_func_ptr to i64**
// CHECK: store i64* [[CAST5]], i64** [[CAST6]], align 8
func test_field_fn_ptr_modify() {
ptr_to_secure_struct!.pointee.secure_func_ptr = returnInt
}

print(test_field_fn_read())
print(test_field_fn_ptr_modify())
3 changes: 3 additions & 0 deletions test/SILGen/Inputs/module.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module PointerAuth {
header "ptrauth_field_fptr_import.h"
}
10 changes: 10 additions & 0 deletions test/SILGen/Inputs/ptrauth_field_fptr_import.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef TEST_C_FUNCTION
#define TEST_C_FUNCTION

struct SecureStruct {
int (*__ptrauth(2, 0, 88)(secure_func_ptr1))();
int (*__ptrauth(3, 0, 66)(secure_func_ptr2))();
};

struct SecureStruct *ptr_to_secure_struct;
#endif
Loading