Skip to content

[IRGen] Upstream pointer auth for value witness tables #74625

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 1 commit into from
Jul 1, 2024
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
3 changes: 3 additions & 0 deletions include/swift/AST/IRGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ struct PointerAuthOptions : clang::PointerAuthOptions {
/// Swift value witness functions.
PointerAuthSchema ValueWitnesses;

/// Pointers to Swift value witness tables stored in type metadata.
PointerAuthSchema ValueWitnessTable;

/// Swift protocol witness functions.
PointerAuthSchema ProtocolWitnesses;

Expand Down
5 changes: 3 additions & 2 deletions include/swift/Remote/MetadataReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -699,10 +699,11 @@ class MetadataReader {
// pointer's referenced address.
TargetValueWitnessTable<Runtime> VWT;
auto ValueWitnessTableAddrAddr = MetadataAddress - sizeof(StoredPointer);
StoredPointer ValueWitnessTableAddr;
StoredSignedPointer SignedValueWitnessTableAddr;
if (!Reader->readInteger(RemoteAddress(ValueWitnessTableAddrAddr),
&ValueWitnessTableAddr))
&SignedValueWitnessTableAddr))
return std::nullopt;
auto ValueWitnessTableAddr = stripSignedPointer(SignedValueWitnessTableAddr);
if (!Reader->readBytes(RemoteAddress(ValueWitnessTableAddr),
(uint8_t *)&VWT, sizeof(VWT)))
return std::nullopt;
Expand Down
68 changes: 56 additions & 12 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "swift/Strings.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
Expand Down Expand Up @@ -4036,7 +4037,14 @@ namespace {

void addValueWitnessTable() {
assert(!isPureObjC());
B.add(asImpl().getValueWitnessTable(false).getValue());

auto wtable = asImpl().getValueWitnessTable(false).getValue();
if (!isa<llvm::ConstantPointerNull>(wtable)) {
auto schema = IGM.getOptions().PointerAuth.ValueWitnessTable;
B.addSignedPointer(wtable, schema, PointerAuthEntity());
} else {
B.add(wtable);
}
}

llvm::Constant *getAddrOfMetaclassObject(ForDefinition_t forDefinition) {
Expand Down Expand Up @@ -5411,19 +5419,36 @@ emitInvariantLoadFromMetadataAtIndex(IRGenFunction &IGF,
/// Given a type metadata pointer, load its value witness table.
llvm::Value *
IRGenFunction::emitValueWitnessTableRefForMetadata(llvm::Value *metadata) {
auto witness = emitInvariantLoadFromMetadataAtIndex(*this, metadata, nullptr,
-1, IGM.WitnessTablePtrTy,
".valueWitnesses");
llvm::Value *addrOfWitnessTablePtr = nullptr;
llvm::LoadInst *loadOfWitnessTablePtr = emitInvariantLoadFromMetadataAtIndex(
*this, metadata, &addrOfWitnessTablePtr, -1, IGM.WitnessTablePtrTy,
".valueWitnesses");

const auto &ptrAuthOpts = IGM.getOptions().PointerAuth;
if (auto schema = IGM.getOptions().PointerAuth.ValueWitnessTable) {
llvm::Value *signedWitnessTablePtr = loadOfWitnessTablePtr;
llvm::Value *witnessTablePtr = emitPointerAuthAuth(
*this, signedWitnessTablePtr,
PointerAuthInfo::emit(*this, schema, addrOfWitnessTablePtr,
PointerAuthEntity()));

// TODO: We might be able to flag witnessTablePtr as dereferencable (see
// below) by adding an attribute (not setting the metadata). However, it
// is unclear if there are any benefits to be had at the cost of
// changing the APIs in multiple places.
return witnessTablePtr;
}

// A value witness table is dereferenceable to the number of value witness
// pointers.

// TODO: If we know the type statically has extra inhabitants, we know
// there are more witnesses.
auto numValueWitnesses
= unsigned(ValueWitness::Last_RequiredValueWitness) + 1;
setDereferenceableLoad(witness,
setDereferenceableLoad(loadOfWitnessTablePtr,
IGM.getPointerSize().getValue() * numValueWitnesses);
return witness;
return loadOfWitnessTablePtr;
}

/// Given a lowered SIL type, load a value witness table that represents its
Expand Down Expand Up @@ -5570,7 +5595,10 @@ namespace {
}

void addValueWitnessTable() {
B.add(asImpl().getValueWitnessTable(false).getValue());
auto vwtPointer = asImpl().getValueWitnessTable(false).getValue();
B.addSignedPointer(vwtPointer,
IGM.getOptions().PointerAuth.ValueWitnessTable,
PointerAuthEntity());
}

llvm::Constant *emitLayoutString() {
Expand Down Expand Up @@ -6051,7 +6079,11 @@ namespace {
}

void addValueWitnessTable() {
B.add(asImpl().getValueWitnessTable(false).getValue());
auto vwtPointer =
asImpl().getValueWitnessTable(/*relative*/ false).getValue();
B.addSignedPointer(vwtPointer,
IGM.getOptions().PointerAuth.ValueWitnessTable,
PointerAuthEntity());
}

llvm::Constant *emitNominalTypeDescriptor() {
Expand Down Expand Up @@ -6533,7 +6565,9 @@ namespace {
? IGM.Context.getAnyObjectType()
: IGM.Context.TheNativeObjectType);
auto wtable = IGM.getAddrOfValueWitnessTable(type);
B.add(wtable);
B.addSignedPointer(wtable,
IGM.getOptions().PointerAuth.ValueWitnessTable,
PointerAuthEntity());
}

void addMetadataFlags() {
Expand Down Expand Up @@ -6604,7 +6638,11 @@ namespace {

void addValueWitnessTable() {
auto type = getTargetType()->getCanonicalType();
B.add(irgen::emitValueWitnessTable(IGM, type, false, false).getValue());
auto vwtPointer =
irgen::emitValueWitnessTable(IGM, type, false, false).getValue();
B.addSignedPointer(vwtPointer,
IGM.getOptions().PointerAuth.ValueWitnessTable,
PointerAuthEntity());
}

void addMetadataFlags() {
Expand Down Expand Up @@ -6643,7 +6681,10 @@ namespace {
}

void addValueWitnessTable() {
B.add(emitValueWitnessTable(/*relative*/ false).getValue());
auto vwtPointer = emitValueWitnessTable(/*relative*/ false).getValue();
B.addSignedPointer(vwtPointer,
IGM.getOptions().PointerAuth.ValueWitnessTable,
PointerAuthEntity());
}

void flagUnfilledFieldOffset() {
Expand All @@ -6670,7 +6711,10 @@ namespace {
}

void addValueWitnessTable() {
B.add(emitValueWitnessTable(/*relative*/ false).getValue());
auto vwtPointer = emitValueWitnessTable(/*relative*/ false).getValue();
B.addSignedPointer(vwtPointer,
IGM.getOptions().PointerAuth.ValueWitnessTable,
PointerAuthEntity());
}

void addPayloadSize() const {
Expand Down
3 changes: 3 additions & 0 deletions lib/IRGen/IRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,9 @@ static void setPointerAuthOptions(PointerAuthOptions &opts,
PointerAuthSchema(codeKey, /*address*/ true, Discrimination::Decl);
opts.ValueWitnesses =
PointerAuthSchema(codeKey, /*address*/ true, Discrimination::Decl);
opts.ValueWitnessTable =
PointerAuthSchema(dataKey, /*address*/ true, Discrimination::Constant,
SpecialPointerAuthDiscriminators::ValueWitnessTable);
opts.ProtocolWitnesses =
PointerAuthSchema(codeKey, /*address*/ true, Discrimination::Decl);
opts.ProtocolAssociatedTypeAccessFunctions =
Expand Down
68 changes: 56 additions & 12 deletions test/IRGen/ptrauth-value-witnesses.sil
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import Builtin

struct S { var a, b, c: Builtin.NativeObject }

// Check the constant discriminators for all value witnesses.

// CHECK: @"$s4test1SVwCP.ptrauth" = private constant {{.*}} i64 55882
// CHECK: @"$s4test1SVwxx.ptrauth" = private constant {{.*}} i64 1272
// CHECK: @"$s4test1SVwcp.ptrauth" = private constant {{.*}} i64 58298
Expand All @@ -16,15 +19,26 @@ struct S { var a, b, c: Builtin.NativeObject }
// CHECK: @"$s4test1SVwet.ptrauth" = private constant {{.*}} i64 24816
// CHECK: @"$s4test1SVwst.ptrauth" = private constant {{.*}} i64 41169

// The pointer to the value witness table is signed too.

// 0x2e3f == 11839 is the constant discriminator for value witness tables.
// CHECK: @"$s4test1SVWV.ptrauth" = private constant {{.*}} i64 11839

sil @test_destroy : $@convention(thin) <T> (@in T) -> () {
bb0(%0 : $*T):
destroy_addr %0 : $*T
%result = tuple ()
return %result : $()
}
// CHECK-LABEL: define swiftcc void @test_destroy(
// CHECK: [[VWT:%.*]] = load ptr,
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 1272)
Expand All @@ -38,8 +52,14 @@ bb0(%0 : $*T, %1 : $*T):
return %result : $()
}
// CHECK-LABEL: define swiftcc void @test_copy_init(
// CHECK: [[VWT:%.*]] = load ptr,
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 58298)
Expand All @@ -53,8 +73,14 @@ bb0(%0 : $*T, %1 : $*T):
return %result : $()
}
// CHECK-LABEL: define swiftcc void @test_take_init(
// CHECK: [[VWT:%.*]] = load ptr,
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 18648)
Expand All @@ -68,8 +94,14 @@ bb0(%0 : $*T, %1 : $*T):
return %result : $()
}
// CHECK-LABEL: define swiftcc void @test_copy_assign(
// CHECK: [[VWT:%.*]] = load ptr,
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 34641)
Expand All @@ -83,8 +115,14 @@ bb0(%0 : $*T, %1 : $*T):
return %result : $()
}
// CHECK-LABEL: define swiftcc void @test_take_assign(
// CHECK: [[VWT:%.*]] = load ptr,
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 61402)
Expand All @@ -100,8 +138,14 @@ bb0(%0 : $*T, %1 : $*T):
return %result : $()
}
// CHECK-LABEL: define swiftcc void @test_destroy_twice(
// CHECK: [[VWT:%.*]] = load ptr,
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 1272)
Expand Down