Skip to content

Commit 387803d

Browse files
ojhuntrjmccall
authored andcommitted
PR 93907: [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 f4c6437 commit 387803d

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

+3342
-92
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>
@@ -1263,6 +1264,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
12631264
/// space.
12641265
QualType removeAddrSpaceQualType(QualType T) const;
12651266

1267+
/// Return the "other" type-specific discriminator for the given type.
1268+
uint16_t
1269+
getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *record);
1270+
12661271
/// Apply Objective-C protocol qualifiers to the given type.
12671272
/// \param allowOnPointerType specifies if we can apply protocol
12681273
/// qualifiers on ObjCObjectPointerType. It can be set to true when
@@ -3424,12 +3429,20 @@ OPT_LIST(V)
34243429
/// Whether a C++ static variable or CUDA/HIP kernel should be externalized.
34253430
bool shouldExternalize(const Decl *D) const;
34263431

3432+
/// Resolve the root record to be used to derive the vtable pointer
3433+
/// authentication policy for the specified record.
3434+
const CXXRecordDecl *baseForVTableAuthentication(const CXXRecordDecl *);
3435+
bool useAbbreviatedThunkName(GlobalDecl virtualMethodDecl,
3436+
StringRef mangledName);
3437+
34273438
StringRef getCUIDHash() const;
34283439

34293440
private:
34303441
/// All OMPTraitInfo objects live in this collection, one per
34313442
/// `pragma omp [begin] declare variant` directive.
34323443
SmallVector<std::unique_ptr<OMPTraitInfo>, 4> OMPTraitInfoVector;
3444+
3445+
llvm::DenseMap<GlobalDecl, llvm::StringSet<>> thunksToBeAbbreviated;
34333446
};
34343447

34353448
/// 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 = [];
@@ -4547,6 +4551,31 @@ def NoRandomizeLayout : InheritableAttr {
45474551
}
45484552
def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;
45494553

4554+
def VTablePointerAuthentication : InheritableAttr {
4555+
let Spellings = [Clang<"ptrauth_vtable_pointer">];
4556+
let Subjects = SubjectList<[CXXRecord]>;
4557+
let Documentation = [Undocumented];
4558+
let StrictEnumParameters = 1;
4559+
let Args = [EnumArgument<"Key", "VPtrAuthKeyType", /*is_string=*/ true,
4560+
["default_key", "no_authentication", "process_dependent",
4561+
"process_independent"],
4562+
["DefaultKey", "NoKey", "ProcessDependent",
4563+
"ProcessIndependent"]>,
4564+
EnumArgument<"AddressDiscrimination", "AddressDiscriminationMode",
4565+
/*is_string=*/ true,
4566+
["default_address_discrimination", "no_address_discrimination",
4567+
"address_discrimination"],
4568+
["DefaultAddressDiscrimination", "NoAddressDiscrimination",
4569+
"AddressDiscrimination"]>,
4570+
EnumArgument<"ExtraDiscrimination", "ExtraDiscrimination",
4571+
/*is_string=*/ true,
4572+
["default_extra_discrimination", "no_extra_discrimination",
4573+
"type_discrimination", "custom_discrimination"],
4574+
["DefaultExtraDiscrimination", "NoExtraDiscrimination",
4575+
"TypeDiscrimination", "CustomDiscrimination"]>,
4576+
IntArgument<"CustomDiscriminationValue", 1>];
4577+
}
4578+
45504579
def FunctionReturnThunks : InheritableAttr,
45514580
TargetSpecificAttr<TargetAnyX86> {
45524581
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
@@ -940,6 +940,13 @@ def warn_ptrauth_auth_null_pointer :
940940
def err_ptrauth_string_not_literal : Error<
941941
"argument must be a string literal%select{| of char type}0">;
942942

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

12192+
// VTable pointer authentication errors
12193+
def err_non_polymorphic_vtable_pointer_auth : Error<
12194+
"cannot set vtable pointer authentication on monomorphic type %0">;
12195+
def err_incomplete_type_vtable_pointer_auth : Error<
12196+
"cannot set vtable pointer authentication on an incomplete type %0">;
12197+
def err_non_top_level_vtable_pointer_auth : Error<
12198+
"cannot set vtable pointer authentication on %0 which is a subclass of polymorphic type %1">;
12199+
def err_duplicated_vtable_pointer_auth : Error<
12200+
"multiple vtable pointer authentication policies on %0">;
12201+
def err_invalid_authentication_key : Error<
12202+
"invalid authentication key %0">;
12203+
def err_invalid_address_discrimination : Error<
12204+
"invalid address discrimination mode %0">;
12205+
def err_invalid_extra_discrimination : Error<
12206+
"invalid extra discrimination selection %0">;
12207+
def err_invalid_custom_discrimination : Error<
12208+
"invalid custom discrimination">;
12209+
def err_missing_custom_discrimination : Error<
12210+
"missing custom discrimination">;
12211+
def err_no_default_vtable_pointer_auth : Error<
12212+
"cannot specify a default vtable pointer authentication "
12213+
"%select{key|address discrimination mode|discriminator}0 with no default set"
12214+
>;
12215+
1218512216
def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must "
1218612217
"have a bit size of at least %select{2|1}0">;
1218712218
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
@@ -47,6 +47,12 @@ class PointerAuthSchema {
4747
/// No additional discrimination.
4848
None,
4949

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

155180
} // 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
/// Given the language and code-generation options that Clang was configured
108112
/// with, set the default LLVM IR attributes for a function definition.
109113
/// The attributes set here are mostly global target-configuration and

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::ConstantInt *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
@@ -4487,6 +4487,10 @@ class Sema final : public SemaBase {
44874487
/// conditions that are needed for the attribute to have an effect.
44884488
void checkIllFormedTrivialABIStruct(CXXRecordDecl &RD);
44894489

4490+
/// Check that VTable Pointer authentication is only being set on the first
4491+
/// first instantiation of the vtable
4492+
void checkIncorrectVTablePointerAuthenticationAttribute(CXXRecordDecl &RD);
4493+
44904494
void ActOnFinishCXXMemberSpecification(Scope *S, SourceLocation RLoc,
44914495
Decl *TagDecl, SourceLocation LBrac,
44924496
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/AArch64TargetParser.h"
9192
#include "llvm/TargetParser/Triple.h"
@@ -3103,6 +3104,17 @@ QualType ASTContext::removeAddrSpaceQualType(QualType T) const {
31033104
return QualType(TypeNode, Quals.getFastQualifiers());
31043105
}
31053106

3107+
uint16_t ASTContext::getPointerAuthVTablePointerDiscriminator(
3108+
const CXXRecordDecl *record) {
3109+
assert(record->isPolymorphic() &&
3110+
"Attempted to get vtable pointer discriminator on a monomorphic type");
3111+
std::unique_ptr<MangleContext> MC(createMangleContext());
3112+
SmallString<256> Str;
3113+
llvm::raw_svector_ostream Out(Str);
3114+
MC->mangleCXXVTable(record, Out);
3115+
return llvm::getPointerAuthStableSipHash(Str.c_str());
3116+
}
3117+
31063118
QualType ASTContext::getObjCGCQualType(QualType T,
31073119
Qualifiers::GC GCAttr) const {
31083120
QualType CanT = getCanonicalType(T);
@@ -13842,3 +13854,77 @@ StringRef ASTContext::getCUIDHash() const {
1384213854
CUIDHash = llvm::utohexstr(llvm::MD5Hash(LangOpts.CUID), /*LowerCase=*/true);
1384313855
return CUIDHash;
1384413856
}
13857+
13858+
const CXXRecordDecl *
13859+
ASTContext::baseForVTableAuthentication(const CXXRecordDecl *thisClass) {
13860+
assert(thisClass);
13861+
assert(thisClass->isPolymorphic());
13862+
const CXXRecordDecl *primaryBase = thisClass;
13863+
while (1) {
13864+
assert(primaryBase);
13865+
assert(primaryBase->isPolymorphic());
13866+
auto &layout = getASTRecordLayout(primaryBase);
13867+
auto base = layout.getPrimaryBase();
13868+
if (!base || base == primaryBase || !base->isPolymorphic())
13869+
break;
13870+
primaryBase = base;
13871+
}
13872+
return primaryBase;
13873+
}
13874+
13875+
bool ASTContext::useAbbreviatedThunkName(GlobalDecl virtualMethodDecl,
13876+
StringRef mangledName) {
13877+
auto method = cast<CXXMethodDecl>(virtualMethodDecl.getDecl());
13878+
assert(method->isVirtual());
13879+
bool defaultIncludesPointerAuth =
13880+
LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics;
13881+
13882+
if (!defaultIncludesPointerAuth)
13883+
return true;
13884+
13885+
auto existing = thunksToBeAbbreviated.find(virtualMethodDecl);
13886+
if (existing != thunksToBeAbbreviated.end())
13887+
return existing->second.contains(mangledName.str());
13888+
13889+
std::unique_ptr<MangleContext> mangler(createMangleContext());
13890+
llvm::StringMap<llvm::SmallVector<std::string, 2>> thunks;
13891+
auto vtableContext = getVTableContext();
13892+
if (const auto *thunkInfos = vtableContext->getThunkInfo(virtualMethodDecl)) {
13893+
auto destructor = dyn_cast<CXXDestructorDecl>(method);
13894+
for (const auto &thunk : *thunkInfos) {
13895+
SmallString<256> elidedName;
13896+
llvm::raw_svector_ostream elidedNameStream(elidedName);
13897+
if (destructor) {
13898+
mangler->mangleCXXDtorThunk(destructor, virtualMethodDecl.getDtorType(),
13899+
thunk, /* elideOverrideInfo */ true,
13900+
elidedNameStream);
13901+
} else {
13902+
mangler->mangleThunk(method, thunk, /* elideOverrideInfo */ true,
13903+
elidedNameStream);
13904+
}
13905+
SmallString<256> mangledName;
13906+
llvm::raw_svector_ostream mangledNameStream(mangledName);
13907+
if (destructor) {
13908+
mangler->mangleCXXDtorThunk(destructor, virtualMethodDecl.getDtorType(),
13909+
thunk, /* elideOverrideInfo */ false,
13910+
mangledNameStream);
13911+
} else {
13912+
mangler->mangleThunk(method, thunk, /* elideOverrideInfo */ false,
13913+
mangledNameStream);
13914+
}
13915+
13916+
if (thunks.find(elidedName) == thunks.end()) {
13917+
thunks[elidedName] = {};
13918+
}
13919+
thunks[elidedName].push_back(std::string(mangledName));
13920+
}
13921+
}
13922+
llvm::StringSet<> simplifiedThunkNames;
13923+
for (auto &thunkList : thunks) {
13924+
llvm::sort(thunkList.second);
13925+
simplifiedThunkNames.insert(thunkList.second[0]);
13926+
}
13927+
bool result = simplifiedThunkNames.contains(mangledName);
13928+
thunksToBeAbbreviated[virtualMethodDecl] = std::move(simplifiedThunkNames);
13929+
return result;
13930+
}

0 commit comments

Comments
 (0)