Skip to content

Commit 30cd90c

Browse files
committed
[IRGen] Upstream pointer auth for value witness tables
This makes Swift emit a signed pointer to the value witness table in type metadata. The original change was done by Varun Gandhi.
1 parent 7c16527 commit 30cd90c

File tree

5 files changed

+121
-26
lines changed

5 files changed

+121
-26
lines changed

include/swift/AST/IRGenOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ struct PointerAuthOptions : clang::PointerAuthOptions {
109109
/// Swift value witness functions.
110110
PointerAuthSchema ValueWitnesses;
111111

112+
/// Pointers to Swift value witness tables stored in type metadata.
113+
PointerAuthSchema ValueWitnessTable;
114+
112115
/// Swift protocol witness functions.
113116
PointerAuthSchema ProtocolWitnesses;
114117

include/swift/Remote/MetadataReader.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -699,10 +699,11 @@ class MetadataReader {
699699
// pointer's referenced address.
700700
TargetValueWitnessTable<Runtime> VWT;
701701
auto ValueWitnessTableAddrAddr = MetadataAddress - sizeof(StoredPointer);
702-
StoredPointer ValueWitnessTableAddr;
702+
StoredSignedPointer SignedValueWitnessTableAddr;
703703
if (!Reader->readInteger(RemoteAddress(ValueWitnessTableAddrAddr),
704-
&ValueWitnessTableAddr))
704+
&SignedValueWitnessTableAddr))
705705
return std::nullopt;
706+
auto ValueWitnessTableAddr = stripSignedPointer(SignedValueWitnessTableAddr);
706707
if (!Reader->readBytes(RemoteAddress(ValueWitnessTableAddr),
707708
(uint8_t *)&VWT, sizeof(VWT)))
708709
return std::nullopt;

lib/IRGen/GenMeta.cpp

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "swift/Strings.h"
3838
#include "clang/AST/Decl.h"
3939
#include "clang/AST/DeclObjC.h"
40+
#include "clang/Basic/TargetInfo.h"
4041
#include "llvm/ADT/SmallString.h"
4142
#include "llvm/IR/DerivedTypes.h"
4243
#include "llvm/IR/Function.h"
@@ -4036,7 +4037,14 @@ namespace {
40364037

40374038
void addValueWitnessTable() {
40384039
assert(!isPureObjC());
4039-
B.add(asImpl().getValueWitnessTable(false).getValue());
4040+
4041+
auto wtable = asImpl().getValueWitnessTable(false).getValue();
4042+
if (wtable != llvm::ConstantPointerNull::get(IGM.WitnessTablePtrTy)) {
4043+
auto schema = IGM.getOptions().PointerAuth.ValueWitnessTable;
4044+
B.addSignedPointer(wtable, schema, PointerAuthEntity());
4045+
} else {
4046+
B.add(wtable);
4047+
}
40404048
}
40414049

40424050
llvm::Constant *getAddrOfMetaclassObject(ForDefinition_t forDefinition) {
@@ -5411,19 +5419,36 @@ emitInvariantLoadFromMetadataAtIndex(IRGenFunction &IGF,
54115419
/// Given a type metadata pointer, load its value witness table.
54125420
llvm::Value *
54135421
IRGenFunction::emitValueWitnessTableRefForMetadata(llvm::Value *metadata) {
5414-
auto witness = emitInvariantLoadFromMetadataAtIndex(*this, metadata, nullptr,
5415-
-1, IGM.WitnessTablePtrTy,
5416-
".valueWitnesses");
5422+
llvm::Value *addrOfWitnessTablePtr = nullptr;
5423+
llvm::LoadInst *loadOfWitnessTablePtr = emitInvariantLoadFromMetadataAtIndex(
5424+
*this, metadata, &addrOfWitnessTablePtr, -1, IGM.WitnessTablePtrTy,
5425+
".valueWitnesses");
5426+
5427+
const auto &ptrAuthOpts = IGM.getOptions().PointerAuth;
5428+
if (auto schema = IGM.getOptions().PointerAuth.ValueWitnessTable) {
5429+
llvm::Value *signedWitnessTablePtr = loadOfWitnessTablePtr;
5430+
llvm::Value *witnessTablePtr = emitPointerAuthAuth(
5431+
*this, signedWitnessTablePtr,
5432+
PointerAuthInfo::emit(*this, schema, addrOfWitnessTablePtr,
5433+
PointerAuthEntity()));
5434+
5435+
// TODO: We might be able to flag witnessTablePtr as dereferencable (see
5436+
// below) by adding an attribute (not setting the metadata). However, it
5437+
// is unclear if there are any benefits to be had at the cost of
5438+
// changing the APIs in multiple places.
5439+
return witnessTablePtr;
5440+
}
5441+
54175442
// A value witness table is dereferenceable to the number of value witness
54185443
// pointers.
54195444

54205445
// TODO: If we know the type statically has extra inhabitants, we know
54215446
// there are more witnesses.
54225447
auto numValueWitnesses
54235448
= unsigned(ValueWitness::Last_RequiredValueWitness) + 1;
5424-
setDereferenceableLoad(witness,
5449+
setDereferenceableLoad(loadOfWitnessTablePtr,
54255450
IGM.getPointerSize().getValue() * numValueWitnesses);
5426-
return witness;
5451+
return loadOfWitnessTablePtr;
54275452
}
54285453

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

55725597
void addValueWitnessTable() {
5573-
B.add(asImpl().getValueWitnessTable(false).getValue());
5598+
auto vwtPointer = asImpl().getValueWitnessTable(false).getValue();
5599+
B.addSignedPointer(vwtPointer,
5600+
IGM.getOptions().PointerAuth.ValueWitnessTable,
5601+
PointerAuthEntity());
55745602
}
55755603

55765604
llvm::Constant *emitLayoutString() {
@@ -6051,7 +6079,11 @@ namespace {
60516079
}
60526080

60536081
void addValueWitnessTable() {
6054-
B.add(asImpl().getValueWitnessTable(false).getValue());
6082+
auto vwtPointer =
6083+
asImpl().getValueWitnessTable(/*relative*/ false).getValue();
6084+
B.addSignedPointer(vwtPointer,
6085+
IGM.getOptions().PointerAuth.ValueWitnessTable,
6086+
PointerAuthEntity());
60556087
}
60566088

60576089
llvm::Constant *emitNominalTypeDescriptor() {
@@ -6533,7 +6565,9 @@ namespace {
65336565
? IGM.Context.getAnyObjectType()
65346566
: IGM.Context.TheNativeObjectType);
65356567
auto wtable = IGM.getAddrOfValueWitnessTable(type);
6536-
B.add(wtable);
6568+
B.addSignedPointer(wtable,
6569+
IGM.getOptions().PointerAuth.ValueWitnessTable,
6570+
PointerAuthEntity());
65376571
}
65386572

65396573
void addMetadataFlags() {
@@ -6604,7 +6638,11 @@ namespace {
66046638

66056639
void addValueWitnessTable() {
66066640
auto type = getTargetType()->getCanonicalType();
6607-
B.add(irgen::emitValueWitnessTable(IGM, type, false, false).getValue());
6641+
auto vwtPointer =
6642+
irgen::emitValueWitnessTable(IGM, type, false, false).getValue();
6643+
B.addSignedPointer(vwtPointer,
6644+
IGM.getOptions().PointerAuth.ValueWitnessTable,
6645+
PointerAuthEntity());
66086646
}
66096647

66106648
void addMetadataFlags() {
@@ -6643,7 +6681,10 @@ namespace {
66436681
}
66446682

66456683
void addValueWitnessTable() {
6646-
B.add(emitValueWitnessTable(/*relative*/ false).getValue());
6684+
auto vwtPointer = emitValueWitnessTable(/*relative*/ false).getValue();
6685+
B.addSignedPointer(vwtPointer,
6686+
IGM.getOptions().PointerAuth.ValueWitnessTable,
6687+
PointerAuthEntity());
66476688
}
66486689

66496690
void flagUnfilledFieldOffset() {
@@ -6670,7 +6711,10 @@ namespace {
66706711
}
66716712

66726713
void addValueWitnessTable() {
6673-
B.add(emitValueWitnessTable(/*relative*/ false).getValue());
6714+
auto vwtPointer = emitValueWitnessTable(/*relative*/ false).getValue();
6715+
B.addSignedPointer(vwtPointer,
6716+
IGM.getOptions().PointerAuth.ValueWitnessTable,
6717+
PointerAuthEntity());
66746718
}
66756719

66766720
void addPayloadSize() const {

lib/IRGen/IRGen.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,9 @@ static void setPointerAuthOptions(PointerAuthOptions &opts,
727727
PointerAuthSchema(codeKey, /*address*/ true, Discrimination::Decl);
728728
opts.ValueWitnesses =
729729
PointerAuthSchema(codeKey, /*address*/ true, Discrimination::Decl);
730+
opts.ValueWitnessTable =
731+
PointerAuthSchema(dataKey, /*address*/ true, Discrimination::Constant,
732+
SpecialPointerAuthDiscriminators::ValueWitnessTable);
730733
opts.ProtocolWitnesses =
731734
PointerAuthSchema(codeKey, /*address*/ true, Discrimination::Decl);
732735
opts.ProtocolAssociatedTypeAccessFunctions =

test/IRGen/ptrauth-value-witnesses.sil

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import Builtin
88

99
struct S { var a, b, c: Builtin.NativeObject }
10+
11+
// Check the constant discriminators for all value witnesses.
12+
1013
// CHECK: @"$s4test1SVwCP.ptrauth" = private constant {{.*}} i64 55882
1114
// CHECK: @"$s4test1SVwxx.ptrauth" = private constant {{.*}} i64 1272
1215
// CHECK: @"$s4test1SVwcp.ptrauth" = private constant {{.*}} i64 58298
@@ -16,15 +19,26 @@ struct S { var a, b, c: Builtin.NativeObject }
1619
// CHECK: @"$s4test1SVwet.ptrauth" = private constant {{.*}} i64 24816
1720
// CHECK: @"$s4test1SVwst.ptrauth" = private constant {{.*}} i64 41169
1821

22+
// The pointer to the value witness table is signed too.
23+
24+
// 0x2e3f == 11839 is the constant discriminator for value witness tables.
25+
// CHECK: @"$s4test1SVWV.ptrauth" = private constant {{.*}} i64 11839
26+
1927
sil @test_destroy : $@convention(thin) <T> (@in T) -> () {
2028
bb0(%0 : $*T):
2129
destroy_addr %0 : $*T
2230
%result = tuple ()
2331
return %result : $()
2432
}
2533
// CHECK-LABEL: define swiftcc void @test_destroy(
26-
// CHECK: [[VWT:%.*]] = load ptr,
27-
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
34+
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
35+
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
36+
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
37+
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
38+
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
39+
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
40+
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
41+
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
2842
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
2943
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
3044
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 1272)
@@ -38,8 +52,14 @@ bb0(%0 : $*T, %1 : $*T):
3852
return %result : $()
3953
}
4054
// CHECK-LABEL: define swiftcc void @test_copy_init(
41-
// CHECK: [[VWT:%.*]] = load ptr,
42-
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
55+
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
56+
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
57+
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
58+
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
59+
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
60+
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
61+
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
62+
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
4363
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
4464
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
4565
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 58298)
@@ -53,8 +73,14 @@ bb0(%0 : $*T, %1 : $*T):
5373
return %result : $()
5474
}
5575
// CHECK-LABEL: define swiftcc void @test_take_init(
56-
// CHECK: [[VWT:%.*]] = load ptr,
57-
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
76+
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
77+
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
78+
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
79+
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
80+
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
81+
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
82+
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
83+
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
5884
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
5985
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
6086
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 18648)
@@ -68,8 +94,14 @@ bb0(%0 : $*T, %1 : $*T):
6894
return %result : $()
6995
}
7096
// CHECK-LABEL: define swiftcc void @test_copy_assign(
71-
// CHECK: [[VWT:%.*]] = load ptr,
72-
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
97+
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
98+
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
99+
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
100+
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
101+
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
102+
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
103+
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
104+
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
73105
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
74106
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
75107
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 34641)
@@ -83,8 +115,14 @@ bb0(%0 : $*T, %1 : $*T):
83115
return %result : $()
84116
}
85117
// CHECK-LABEL: define swiftcc void @test_take_assign(
86-
// CHECK: [[VWT:%.*]] = load ptr,
87-
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
118+
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
119+
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
120+
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
121+
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
122+
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
123+
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
124+
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
125+
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
88126
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
89127
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
90128
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 61402)
@@ -100,8 +138,14 @@ bb0(%0 : $*T, %1 : $*T):
100138
return %result : $()
101139
}
102140
// CHECK-LABEL: define swiftcc void @test_destroy_twice(
103-
// CHECK: [[VWT:%.*]] = load ptr,
104-
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32
141+
// CHECK: [[SIGNED_VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr {{%.*}}, i64 -1
142+
// CHECK: [[SIGNED_VWT:%.*]] = load ptr, ptr [[SIGNED_VWT_ADDR]]
143+
// CHECK: [[SIGNED_VWT_ADDR_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT_ADDR]]
144+
// CHECK: [[BLENDED_ADDR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SIGNED_VWT_ADDR_INT]], i64 11839)
145+
// CHECK: [[SIGNED_VWT_INT:%.*]] = ptrtoint ptr [[SIGNED_VWT]]
146+
// CHECK: [[AUTHENTICATED_VWT_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[SIGNED_VWT_INT]], i32 2, i64 [[BLENDED_ADDR]])
147+
// CHECK: [[AUTHENTICATED_VWT:%.*]] = inttoptr i64 [[AUTHENTICATED_VWT_INT]]
148+
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds ptr, ptr [[AUTHENTICATED_VWT]], i32
105149
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]], align
106150
// CHECK-NEXT: [[T1:%.*]] = ptrtoint ptr [[SLOT]] to i64
107151
// CHECK-NEXT: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 1272)

0 commit comments

Comments
 (0)