Skip to content

Commit 08e713d

Browse files
ojhuntrjmccall
andcommitted
[clang] Implement pointer authentication for C++ virtual functions, v-tables, and VTTs.
This also implements the ptrauth_vtable_pointer attribute to allow overriding the default ptrauth schema for vtable pointers. Co-Authored-By: John McCall <[email protected]>
1 parent 23e9661 commit 08e713d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+3330
-93
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "llvm/ADT/SmallVector.h"
3838
#include "llvm/ADT/StringMap.h"
3939
#include "llvm/ADT/StringRef.h"
40+
#include "llvm/ADT/StringSet.h"
4041
#include "llvm/ADT/TinyPtrVector.h"
4142
#include "llvm/Support/TypeSize.h"
4243
#include <optional>
@@ -1261,6 +1262,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
12611262
/// space.
12621263
QualType removeAddrSpaceQualType(QualType T) const;
12631264

1265+
/// Return the "other" type-specific discriminator for the given type.
1266+
uint16_t
1267+
getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *record);
1268+
12641269
/// Apply Objective-C protocol qualifiers to the given type.
12651270
/// \param allowOnPointerType specifies if we can apply protocol
12661271
/// qualifiers on ObjCObjectPointerType. It can be set to true when
@@ -3418,12 +3423,20 @@ OPT_LIST(V)
34183423
/// Whether a C++ static variable or CUDA/HIP kernel should be externalized.
34193424
bool shouldExternalize(const Decl *D) const;
34203425

3426+
/// Resolve the root record to be used to derive the vtable pointer
3427+
/// authentication policy for the specified record.
3428+
const CXXRecordDecl *baseForVTableAuthentication(const CXXRecordDecl *);
3429+
bool useAbbreviatedThunkName(GlobalDecl virtualMethodDecl,
3430+
StringRef mangledName);
3431+
34213432
StringRef getCUIDHash() const;
34223433

34233434
private:
34243435
/// All OMPTraitInfo objects live in this collection, one per
34253436
/// `pragma omp [begin] declare variant` directive.
34263437
SmallVector<std::unique_ptr<OMPTraitInfo>, 4> OMPTraitInfoVector;
3438+
3439+
llvm::DenseMap<GlobalDecl, llvm::StringSet<>> thunksToBeAbbreviated;
34273440
};
34283441

34293442
/// Insertion operator for diagnostics.

clang/include/clang/AST/Mangle.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,15 @@ class MangleContext {
130130
// FIXME: consider replacing raw_ostream & with something like SmallString &.
131131
void mangleName(GlobalDecl GD, raw_ostream &);
132132
virtual void mangleCXXName(GlobalDecl GD, raw_ostream &) = 0;
133-
virtual void mangleThunk(const CXXMethodDecl *MD,
134-
const ThunkInfo &Thunk,
135-
raw_ostream &) = 0;
133+
virtual void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk,
134+
bool elideOverrideInfo, raw_ostream &) = 0;
136135
virtual void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type,
137-
const ThisAdjustment &ThisAdjustment,
138-
raw_ostream &) = 0;
136+
const ThunkInfo &Thunk,
137+
bool elideOverrideInfo, raw_ostream &) = 0;
139138
virtual void mangleReferenceTemporary(const VarDecl *D,
140139
unsigned ManglingNumber,
141140
raw_ostream &) = 0;
141+
virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0;
142142
virtual void mangleCXXRTTI(QualType T, raw_ostream &) = 0;
143143
virtual void mangleCXXRTTIName(QualType T, raw_ostream &,
144144
bool NormalizeIntegers = false) = 0;
@@ -192,7 +192,6 @@ class ItaniumMangleContext : public MangleContext {
192192
bool IsAux = false)
193193
: MangleContext(C, D, MK_Itanium, IsAux) {}
194194

195-
virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0;
196195
virtual void mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) = 0;
197196
virtual void mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset,
198197
const CXXRecordDecl *Type,

clang/include/clang/Basic/Attr.td

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,10 @@ class Attr {
680680
bit PragmaAttributeSupport;
681681
// Set to true if this attribute accepts parameter pack expansion expressions.
682682
bit AcceptsExprPack = 0;
683+
// To support multiple enum parameters to an attribute without breaking
684+
// our existing general parsing we need to have a separate flag that
685+
// opts an attribute into strict parsing of attribute parameters
686+
bit StrictEnumParameters = 0;
683687
// Lists language options, one of which is required to be true for the
684688
// attribute to be applicable. If empty, no language options are required.
685689
list<LangOpt> LangOpts = [];
@@ -4546,6 +4550,31 @@ def NoRandomizeLayout : InheritableAttr {
45464550
}
45474551
def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;
45484552

4553+
def VTablePointerAuthentication : InheritableAttr {
4554+
let Spellings = [Clang<"ptrauth_vtable_pointer">];
4555+
let Subjects = SubjectList<[CXXRecord]>;
4556+
let Documentation = [Undocumented];
4557+
let StrictEnumParameters = 1;
4558+
let Args = [EnumArgument<"Key", "VPtrAuthKeyType", /*is_string=*/ true,
4559+
["default_key", "no_authentication", "process_dependent",
4560+
"process_independent"],
4561+
["DefaultKey", "NoKey", "ProcessDependent",
4562+
"ProcessIndependent"]>,
4563+
EnumArgument<"AddressDiscrimination", "AddressDiscriminationMode",
4564+
/*is_string=*/ true,
4565+
["default_address_discrimination", "no_address_discrimination",
4566+
"address_discrimination"],
4567+
["DefaultAddressDiscrimination", "NoAddressDiscrimination",
4568+
"AddressDiscrimination"]>,
4569+
EnumArgument<"ExtraDiscrimination", "ExtraDiscrimination",
4570+
/*is_string=*/ true,
4571+
["default_extra_discrimination", "no_extra_discrimination",
4572+
"type_discrimination", "custom_discrimination"],
4573+
["DefaultExtraDiscrimination", "NoExtraDiscrimination",
4574+
"TypeDiscrimination", "CustomDiscrimination"]>,
4575+
IntArgument<"CustomDiscriminationValue", 1>];
4576+
}
4577+
45494578
def FunctionReturnThunks : InheritableAttr,
45504579
TargetSpecificAttr<TargetAnyX86> {
45514580
let Spellings = [GCC<"function_return">];

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,13 @@ def warn_ptrauth_auth_null_pointer :
938938
def err_ptrauth_string_not_literal : Error<
939939
"argument must be a string literal%select{| of char type}0">;
940940

941+
def note_ptrauth_virtual_function_pointer_incomplete_arg_ret :
942+
Note<"cannot take an address of a virtual member function if its return or "
943+
"argument types are incomplete">;
944+
def note_ptrauth_virtual_function_incomplete_arg_ret_type :
945+
Note<"%0 is incomplete">;
946+
947+
941948
/// main()
942949
// static main() is not an error in C, just in C++.
943950
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -12175,6 +12182,30 @@ def warn_cuda_maxclusterrank_sm_90 : Warning<
1217512182
"maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring "
1217612183
"%1 attribute">, InGroup<IgnoredAttributes>;
1217712184

12185+
// VTable pointer authentication errors
12186+
def err_non_polymorphic_vtable_pointer_auth : Error<
12187+
"cannot set vtable pointer authentication on monomorphic type %0">;
12188+
def err_incomplete_type_vtable_pointer_auth : Error<
12189+
"cannot set vtable pointer authentication on an incomplete type %0">;
12190+
def err_non_top_level_vtable_pointer_auth : Error<
12191+
"cannot set vtable pointer authentication on %0 which is a subclass of polymorphic type %1">;
12192+
def err_duplicated_vtable_pointer_auth : Error<
12193+
"multiple vtable pointer authentication policies on %0">;
12194+
def err_invalid_authentication_key : Error<
12195+
"invalid authentication key %0">;
12196+
def err_invalid_address_discrimination : Error<
12197+
"invalid address discrimination mode %0">;
12198+
def err_invalid_extra_discrimination : Error<
12199+
"invalid extra discrimination selection %0">;
12200+
def err_invalid_custom_discrimination : Error<
12201+
"invalid custom discrimination">;
12202+
def err_missing_custom_discrimination : Error<
12203+
"missing custom discrimination">;
12204+
def err_no_default_vtable_pointer_auth : Error<
12205+
"cannot specify a default vtable pointer authentication "
12206+
"%select{key|address discrimination mode|discriminator}0 with no default set"
12207+
>;
12208+
1217812209
def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must "
1217912210
"have a bit size of at least %select{2|1}0">;
1218012211
def err_bit_int_max_size : Error<"%select{signed|unsigned}0 _BitInt of bit "

clang/include/clang/Basic/PointerAuthOptions.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ class PointerAuthSchema {
4949
/// No additional discrimination.
5050
None,
5151

52+
/// Include a hash of the entity's type.
53+
Type,
54+
55+
/// Include a hash of the entity's identity.
56+
Decl,
57+
5258
/// Discriminate using a constant value.
5359
Constant,
5460
};
@@ -152,6 +158,25 @@ class PointerAuthSchema {
152158
struct PointerAuthOptions {
153159
/// The ABI for C function pointers.
154160
PointerAuthSchema FunctionPointers;
161+
162+
/// The ABI for C++ virtual table pointers (the pointer to the table
163+
/// itself) as installed in an actual class instance.
164+
PointerAuthSchema CXXVTablePointers;
165+
166+
/// TypeInfo has external ABI requirements and is emitted without
167+
/// actually having parsed the libcxx definition, so we can't simply
168+
/// perform a look up. The settings for this should match the exact
169+
/// specification in type_info.h
170+
PointerAuthSchema CXXTypeInfoVTablePointer;
171+
172+
/// The ABI for C++ virtual table pointers as installed in a VTT.
173+
PointerAuthSchema CXXVTTVTablePointers;
174+
175+
/// The ABI for most C++ virtual function pointers, i.e. v-table entries.
176+
PointerAuthSchema CXXVirtualFunctionPointers;
177+
178+
/// The ABI for variadic C++ virtual function pointers.
179+
PointerAuthSchema CXXVirtualVariadicFunctionPointers;
155180
};
156181

157182
} // end namespace clang

clang/include/clang/Basic/Thunk.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,20 @@ struct ThunkInfo {
169169
/// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using
170170
/// an ABI-specific comparator.
171171
const CXXMethodDecl *Method;
172+
const Type *ThisType { nullptr };
172173

173174
ThunkInfo() : Method(nullptr) {}
174175

175176
ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return,
177+
const Type *thisType,
176178
const CXXMethodDecl *Method = nullptr)
177-
: This(This), Return(Return), Method(Method) {}
179+
: This(This), Return(Return), Method(Method),
180+
ThisType(thisType) {}
178181

179182
friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) {
180183
return LHS.This == RHS.This && LHS.Return == RHS.Return &&
181-
LHS.Method == RHS.Method;
184+
LHS.Method == RHS.Method &&
185+
LHS.ThisType == RHS.ThisType;
182186
}
183187

184188
bool isEmpty() const {

clang/include/clang/CodeGen/CodeGenABITypes.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class CXXConstructorDecl;
4242
class CXXDestructorDecl;
4343
class CXXRecordDecl;
4444
class CXXMethodDecl;
45+
class GlobalDecl;
4546
class ObjCMethodDecl;
4647
class ObjCProtocolDecl;
4748

@@ -104,6 +105,9 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T);
104105
unsigned getLLVMFieldNumber(CodeGenModule &CGM,
105106
const RecordDecl *RD, const FieldDecl *FD);
106107

108+
/// Return a declaration discriminator for the given global decl.
109+
uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD);
110+
107111
/// Return a signed constant pointer.
108112
llvm::Constant *getConstantSignedPointer(CodeGenModule &CGM,
109113
llvm::Constant *pointer,

clang/include/clang/CodeGen/ConstantInitBuilder.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@
2525
#include <vector>
2626

2727
namespace clang {
28-
namespace CodeGen {
28+
class GlobalDecl;
29+
class PointerAuthSchema;
30+
class QualType;
2931

32+
namespace CodeGen {
3033
class CodeGenModule;
3134

3235
/// A convenience builder class for complex constant initializers,
@@ -199,6 +202,17 @@ class ConstantAggregateBuilderBase {
199202
add(llvm::ConstantInt::get(intTy, value, isSigned));
200203
}
201204

205+
/// Add a signed pointer using the given pointer authentication schema.
206+
void addSignedPointer(llvm::Constant *pointer,
207+
const PointerAuthSchema &schema, GlobalDecl calleeDecl,
208+
QualType calleeType);
209+
210+
/// Add a signed pointer using the given pointer authentication schema.
211+
void addSignedPointer(llvm::Constant *pointer,
212+
unsigned key,
213+
bool useAddressDiscrimination,
214+
llvm::Constant *otherDiscriminator);
215+
202216
/// Add a null pointer of a specific type.
203217
void addNullPointer(llvm::PointerType *ptrTy) {
204218
add(llvm::ConstantPointerNull::get(ptrTy));

clang/include/clang/InstallAPI/Visitor.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ class InstallAPIVisitor final : public ASTConsumer,
6060
std::string getMangledName(const NamedDecl *D) const;
6161
std::string getBackendMangledName(llvm::Twine Name) const;
6262
std::string getMangledCXXVTableName(const CXXRecordDecl *D) const;
63-
std::string getMangledCXXThunk(const GlobalDecl &D,
64-
const ThunkInfo &Thunk) const;
63+
std::string getMangledCXXThunk(const GlobalDecl &D, const ThunkInfo &Thunk,
64+
bool ElideOverrideInfo) const;
6565
std::string getMangledCXXRTTI(const CXXRecordDecl *D) const;
6666
std::string getMangledCXXRTTIName(const CXXRecordDecl *D) const;
6767
std::string getMangledCtorDtor(const CXXMethodDecl *D, int Type) const;

clang/include/clang/Sema/Sema.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4401,6 +4401,10 @@ class Sema final : public SemaBase {
44014401
/// conditions that are needed for the attribute to have an effect.
44024402
void checkIllFormedTrivialABIStruct(CXXRecordDecl &RD);
44034403

4404+
/// Check that VTable Pointer authentication is only being set on the first
4405+
/// first instantiation of the vtable
4406+
void checkIncorrectVTablePointerAuthenticationAttribute(CXXRecordDecl &RD);
4407+
44044408
void ActOnFinishCXXMemberSpecification(Scope *S, SourceLocation RLoc,
44054409
Decl *TagDecl, SourceLocation LBrac,
44064410
SourceLocation RBrac,

clang/lib/AST/ASTContext.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
#include "llvm/Support/ErrorHandling.h"
8787
#include "llvm/Support/MD5.h"
8888
#include "llvm/Support/MathExtras.h"
89+
#include "llvm/Support/SipHash.h"
8990
#include "llvm/Support/raw_ostream.h"
9091
#include "llvm/TargetParser/Triple.h"
9192
#include <algorithm>
@@ -3088,6 +3089,17 @@ QualType ASTContext::removeAddrSpaceQualType(QualType T) const {
30883089
return QualType(TypeNode, Quals.getFastQualifiers());
30893090
}
30903091

3092+
uint16_t ASTContext::getPointerAuthVTablePointerDiscriminator(
3093+
const CXXRecordDecl *record) {
3094+
assert(record->isPolymorphic() &&
3095+
"Attempted to get vtable pointer discriminator on a monomorphic type");
3096+
std::unique_ptr<MangleContext> MC(createMangleContext());
3097+
SmallString<256> Str;
3098+
llvm::raw_svector_ostream Out(Str);
3099+
MC->mangleCXXVTable(record, Out);
3100+
return llvm::getPointerAuthStableSipHash16(Str.c_str());
3101+
}
3102+
30913103
QualType ASTContext::getObjCGCQualType(QualType T,
30923104
Qualifiers::GC GCAttr) const {
30933105
QualType CanT = getCanonicalType(T);
@@ -13812,3 +13824,77 @@ StringRef ASTContext::getCUIDHash() const {
1381213824
CUIDHash = llvm::utohexstr(llvm::MD5Hash(LangOpts.CUID), /*LowerCase=*/true);
1381313825
return CUIDHash;
1381413826
}
13827+
13828+
const CXXRecordDecl *
13829+
ASTContext::baseForVTableAuthentication(const CXXRecordDecl *thisClass) {
13830+
assert(thisClass);
13831+
assert(thisClass->isPolymorphic());
13832+
const CXXRecordDecl *primaryBase = thisClass;
13833+
while (1) {
13834+
assert(primaryBase);
13835+
assert(primaryBase->isPolymorphic());
13836+
auto &layout = getASTRecordLayout(primaryBase);
13837+
auto base = layout.getPrimaryBase();
13838+
if (!base || base == primaryBase || !base->isPolymorphic())
13839+
break;
13840+
primaryBase = base;
13841+
}
13842+
return primaryBase;
13843+
}
13844+
13845+
bool ASTContext::useAbbreviatedThunkName(GlobalDecl virtualMethodDecl,
13846+
StringRef mangledName) {
13847+
auto method = cast<CXXMethodDecl>(virtualMethodDecl.getDecl());
13848+
assert(method->isVirtual());
13849+
bool defaultIncludesPointerAuth =
13850+
LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics;
13851+
13852+
if (!defaultIncludesPointerAuth)
13853+
return true;
13854+
13855+
auto existing = thunksToBeAbbreviated.find(virtualMethodDecl);
13856+
if (existing != thunksToBeAbbreviated.end())
13857+
return existing->second.contains(mangledName.str());
13858+
13859+
std::unique_ptr<MangleContext> mangler(createMangleContext());
13860+
llvm::StringMap<llvm::SmallVector<std::string, 2>> thunks;
13861+
auto vtableContext = getVTableContext();
13862+
if (const auto *thunkInfos = vtableContext->getThunkInfo(virtualMethodDecl)) {
13863+
auto destructor = dyn_cast<CXXDestructorDecl>(method);
13864+
for (const auto &thunk : *thunkInfos) {
13865+
SmallString<256> elidedName;
13866+
llvm::raw_svector_ostream elidedNameStream(elidedName);
13867+
if (destructor) {
13868+
mangler->mangleCXXDtorThunk(destructor, virtualMethodDecl.getDtorType(),
13869+
thunk, /* elideOverrideInfo */ true,
13870+
elidedNameStream);
13871+
} else {
13872+
mangler->mangleThunk(method, thunk, /* elideOverrideInfo */ true,
13873+
elidedNameStream);
13874+
}
13875+
SmallString<256> mangledName;
13876+
llvm::raw_svector_ostream mangledNameStream(mangledName);
13877+
if (destructor) {
13878+
mangler->mangleCXXDtorThunk(destructor, virtualMethodDecl.getDtorType(),
13879+
thunk, /* elideOverrideInfo */ false,
13880+
mangledNameStream);
13881+
} else {
13882+
mangler->mangleThunk(method, thunk, /* elideOverrideInfo */ false,
13883+
mangledNameStream);
13884+
}
13885+
13886+
if (thunks.find(elidedName) == thunks.end()) {
13887+
thunks[elidedName] = {};
13888+
}
13889+
thunks[elidedName].push_back(std::string(mangledName));
13890+
}
13891+
}
13892+
llvm::StringSet<> simplifiedThunkNames;
13893+
for (auto &thunkList : thunks) {
13894+
llvm::sort(thunkList.second);
13895+
simplifiedThunkNames.insert(thunkList.second[0]);
13896+
}
13897+
bool result = simplifiedThunkNames.contains(mangledName);
13898+
thunksToBeAbbreviated[virtualMethodDecl] = std::move(simplifiedThunkNames);
13899+
return result;
13900+
}

0 commit comments

Comments
 (0)