Skip to content

Commit 1b8ab2f

Browse files
ojhuntrjmccallahmedbougacha
authored
[clang] Implement pointer authentication for C++ virtual functions, v-tables, and VTTs (#94056)
Virtual function pointer entries in v-tables are signed with address discrimination in addition to declaration-based discrimination, where an integer discriminator the string hash (see `ptrauth_string_discriminator`) of the mangled name of the overridden method. This notably provides diversity based on the full signature of the overridden method, including the method name and parameter types. This patch introduces ItaniumVTableContext logic to find the original declaration of the overridden method. On AArch64, these pointers are signed using the `IA` key (the process-independent code key.) V-table pointers can be signed with either no discrimination, or a similar scheme using address and decl-based discrimination. In this case, the integer discriminator is the string hash of the mangled v-table identifier of the class that originally introduced the vtable pointer. On AArch64, these pointers are signed using the `DA` key (the process-independent data key.) Not using discrimination allows attackers to simply copy valid v-table pointers from one object to another. However, using a uniform discriminator of 0 does have positive performance and code-size implications on AArch64, and diversity for the most important v-table access pattern (virtual dispatch) is already better assured by the signing schemas used on the virtual functions. It is also known that some code in practice copies objects containing v-tables with `memcpy`, and while this is not permitted formally, it is something that may be invasive to eliminate. This is controlled by: ``` -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination ``` In addition, this provides fine-grained controls in the ptrauth_vtable_pointer attribute, which allows overriding the default ptrauth schema for vtable pointers on a given class hierarchy, e.g.: ``` [[clang::ptrauth_vtable_pointer(no_authentication, no_address_discrimination, no_extra_discrimination)]] [[clang::ptrauth_vtable_pointer(default_key, default_address_discrimination, custom_discrimination, 0xf00d)]] ``` The override is then mangled as a parametrized vendor extension: ``` "__vtptrauth" I <key> <addressDiscriminated> <extraDiscriminator> E ``` To support this attribute, this patch adds a small extension to the attribute-emitter tablegen backend. Note that there are known areas where signing is either missing altogether or can be strengthened. Some will be addressed in later changes (e.g., member function pointers, some RTTI). `dynamic_cast` in particular is handled by emitting an artificial v-table pointer load (in a way that always authenticates it) before the runtime call itself, as the runtime doesn't have enough information today to properly authenticate it. Instead, the runtime is currently expected to strip the v-table pointer. --------- Co-authored-by: John McCall <[email protected]> Co-authored-by: Ahmed Bougacha <[email protected]>
1 parent 326ba38 commit 1b8ab2f

Some content is hidden

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

54 files changed

+3343
-98
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 15 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>
@@ -1277,6 +1278,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
12771278
/// space.
12781279
QualType removeAddrSpaceQualType(QualType T) const;
12791280

1281+
/// Return the "other" discriminator used for the pointer auth schema used for
1282+
/// vtable pointers in instances of the requested type.
1283+
uint16_t
1284+
getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD);
1285+
12801286
/// Apply Objective-C protocol qualifiers to the given type.
12811287
/// \param allowOnPointerType specifies if we can apply protocol
12821288
/// qualifiers on ObjCObjectPointerType. It can be set to true when
@@ -3438,12 +3444,21 @@ OPT_LIST(V)
34383444
/// Whether a C++ static variable or CUDA/HIP kernel should be externalized.
34393445
bool shouldExternalize(const Decl *D) const;
34403446

3447+
/// Resolve the root record to be used to derive the vtable pointer
3448+
/// authentication policy for the specified record.
3449+
const CXXRecordDecl *
3450+
baseForVTableAuthentication(const CXXRecordDecl *ThisClass);
3451+
bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
3452+
StringRef MangledName);
3453+
34413454
StringRef getCUIDHash() const;
34423455

34433456
private:
34443457
/// All OMPTraitInfo objects live in this collection, one per
34453458
/// `pragma omp [begin] declare variant` directive.
34463459
SmallVector<std::unique_ptr<OMPTraitInfo>, 4> OMPTraitInfoVector;
3460+
3461+
llvm::DenseMap<GlobalDecl, llvm::StringSet<>> ThunksToBeAbbreviated;
34473462
};
34483463

34493464
/// Insertion operator for diagnostics.

clang/include/clang/AST/GlobalDecl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ class GlobalDecl {
145145
LHS.MultiVersionIndex == RHS.MultiVersionIndex;
146146
}
147147

148+
bool operator!=(const GlobalDecl &Other) const {
149+
return !(*this == Other);
150+
}
151+
148152
void *getAsOpaquePtr() const { return Value.getOpaqueValue(); }
149153

150154
explicit operator bool() const { return getAsOpaquePtr(); }

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/AST/VTableBuilder.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ class VTableContextBase {
361361
};
362362

363363
class ItaniumVTableContext : public VTableContextBase {
364+
public:
365+
typedef llvm::DenseMap<const CXXMethodDecl *, const CXXMethodDecl *>
366+
OriginalMethodMapTy;
367+
364368
private:
365369

366370
/// Contains the index (relative to the vtable address point)
@@ -384,6 +388,10 @@ class ItaniumVTableContext : public VTableContextBase {
384388
VirtualBaseClassOffsetOffsetsMapTy;
385389
VirtualBaseClassOffsetOffsetsMapTy VirtualBaseClassOffsetOffsets;
386390

391+
/// Map from a virtual method to the nearest method in the primary base class
392+
/// chain that it overrides.
393+
OriginalMethodMapTy OriginalMethodMap;
394+
387395
void computeVTableRelatedInformation(const CXXRecordDecl *RD) override;
388396

389397
public:
@@ -425,6 +433,27 @@ class ItaniumVTableContext : public VTableContextBase {
425433
CharUnits getVirtualBaseOffsetOffset(const CXXRecordDecl *RD,
426434
const CXXRecordDecl *VBase);
427435

436+
/// Return the method that added the v-table slot that will be used to call
437+
/// the given method.
438+
///
439+
/// In the Itanium ABI, where overrides always cause methods to be added to
440+
/// the primary v-table if they're not already there, this will be the first
441+
/// declaration in the primary base class chain for which the return type
442+
/// adjustment is trivial.
443+
GlobalDecl findOriginalMethod(GlobalDecl GD);
444+
445+
const CXXMethodDecl *findOriginalMethodInMap(const CXXMethodDecl *MD) const;
446+
447+
void setOriginalMethod(const CXXMethodDecl *Key, const CXXMethodDecl *Val) {
448+
OriginalMethodMap[Key] = Val;
449+
}
450+
451+
/// This method is reserved for the implementation and shouldn't be used
452+
/// directly.
453+
const OriginalMethodMapTy &getOriginalMethodMap() {
454+
return OriginalMethodMap;
455+
}
456+
428457
static bool classof(const VTableContextBase *VT) {
429458
return !VT->isMicrosoft();
430459
}

clang/include/clang/Basic/Attr.td

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,10 @@ class Attr {
686686
bit PragmaAttributeSupport;
687687
// Set to true if this attribute accepts parameter pack expansion expressions.
688688
bit AcceptsExprPack = 0;
689+
// To support multiple enum parameters to an attribute without breaking
690+
// our existing general parsing we need to have a separate flag that
691+
// opts an attribute into strict parsing of attribute parameters
692+
bit StrictEnumParameters = 0;
689693
// Lists language options, one of which is required to be true for the
690694
// attribute to be applicable. If empty, no language options are required.
691695
list<LangOpt> LangOpts = [];
@@ -4576,6 +4580,31 @@ def NoRandomizeLayout : InheritableAttr {
45764580
}
45774581
def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;
45784582

4583+
def VTablePointerAuthentication : InheritableAttr {
4584+
let Spellings = [Clang<"ptrauth_vtable_pointer">];
4585+
let Subjects = SubjectList<[CXXRecord]>;
4586+
let Documentation = [Undocumented];
4587+
let StrictEnumParameters = 1;
4588+
let Args = [EnumArgument<"Key", "VPtrAuthKeyType", /*is_string=*/ true,
4589+
["default_key", "no_authentication", "process_dependent",
4590+
"process_independent"],
4591+
["DefaultKey", "NoKey", "ProcessDependent",
4592+
"ProcessIndependent"]>,
4593+
EnumArgument<"AddressDiscrimination", "AddressDiscriminationMode",
4594+
/*is_string=*/ true,
4595+
["default_address_discrimination", "no_address_discrimination",
4596+
"address_discrimination"],
4597+
["DefaultAddressDiscrimination", "NoAddressDiscrimination",
4598+
"AddressDiscrimination"]>,
4599+
EnumArgument<"ExtraDiscrimination", "ExtraDiscrimination",
4600+
/*is_string=*/ true,
4601+
["default_extra_discrimination", "no_extra_discrimination",
4602+
"type_discrimination", "custom_discrimination"],
4603+
["DefaultExtraDiscrimination", "NoExtraDiscrimination",
4604+
"TypeDiscrimination", "CustomDiscrimination"]>,
4605+
IntArgument<"CustomDiscriminationValue", 1>];
4606+
}
4607+
45794608
def FunctionReturnThunks : InheritableAttr,
45804609
TargetSpecificAttr<TargetAnyX86> {
45814610
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">,
@@ -12220,6 +12227,30 @@ def warn_cuda_maxclusterrank_sm_90 : Warning<
1222012227
"maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring "
1222112228
"%1 attribute">, InGroup<IgnoredAttributes>;
1222212229

12230+
// VTable pointer authentication errors
12231+
def err_non_polymorphic_vtable_pointer_auth : Error<
12232+
"cannot set vtable pointer authentication on monomorphic type %0">;
12233+
def err_incomplete_type_vtable_pointer_auth : Error<
12234+
"cannot set vtable pointer authentication on an incomplete type %0">;
12235+
def err_non_top_level_vtable_pointer_auth : Error<
12236+
"cannot set vtable pointer authentication on %0 which is a subclass of polymorphic type %1">;
12237+
def err_duplicated_vtable_pointer_auth : Error<
12238+
"multiple vtable pointer authentication policies on %0">;
12239+
def err_invalid_authentication_key : Error<
12240+
"invalid authentication key %0">;
12241+
def err_invalid_address_discrimination : Error<
12242+
"invalid address discrimination mode %0">;
12243+
def err_invalid_extra_discrimination : Error<
12244+
"invalid extra discrimination selection %0">;
12245+
def err_invalid_custom_discrimination : Error<
12246+
"invalid custom discrimination">;
12247+
def err_missing_custom_discrimination : Error<
12248+
"missing custom discrimination">;
12249+
def err_no_default_vtable_pointer_auth : Error<
12250+
"cannot specify a default vtable pointer authentication "
12251+
"%select{key|address discrimination mode|discriminator}0 with no default set"
12252+
>;
12253+
1222312254
def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must "
1222412255
"have a bit size of at least %select{2|1}0">;
1222512256
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: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,20 +162,24 @@ struct ThunkInfo {
162162

163163
/// Holds a pointer to the overridden method this thunk is for,
164164
/// if needed by the ABI to distinguish different thunks with equal
165-
/// adjustments. Otherwise, null.
165+
/// adjustments.
166+
/// In the Itanium ABI, this field can hold the method that created the
167+
/// vtable entry for this thunk.
168+
/// Otherwise, null.
166169
/// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using
167170
/// an ABI-specific comparator.
168171
const CXXMethodDecl *Method;
172+
const Type *ThisType;
169173

170-
ThunkInfo() : Method(nullptr) {}
174+
ThunkInfo() : Method(nullptr), ThisType(nullptr) {}
171175

172176
ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return,
173-
const CXXMethodDecl *Method = nullptr)
174-
: This(This), Return(Return), Method(Method) {}
177+
const Type *ThisT, const CXXMethodDecl *Method = nullptr)
178+
: This(This), Return(Return), Method(Method), ThisType(ThisT) {}
175179

176180
friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) {
177181
return LHS.This == RHS.This && LHS.Return == RHS.Return &&
178-
LHS.Method == RHS.Method;
182+
LHS.Method == RHS.Method && LHS.ThisType == RHS.ThisType;
179183
}
180184

181185
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: 9 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,11 @@ 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+
202210
/// Add a null pointer of a specific type.
203211
void addNullPointer(llvm::PointerType *ptrTy) {
204212
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
@@ -4566,6 +4566,10 @@ class Sema final : public SemaBase {
45664566
/// conditions that are needed for the attribute to have an effect.
45674567
void checkIllFormedTrivialABIStruct(CXXRecordDecl &RD);
45684568

4569+
/// Check that VTable Pointer authentication is only being set on the first
4570+
/// first instantiation of the vtable
4571+
void checkIncorrectVTablePointerAuthenticationAttribute(CXXRecordDecl &RD);
4572+
45694573
void ActOnFinishCXXMemberSpecification(Scope *S, SourceLocation RLoc,
45704574
Decl *TagDecl, SourceLocation LBrac,
45714575
SourceLocation RBrac,

0 commit comments

Comments
 (0)