Skip to content

[clang] Implement pointer authentication for C++ virtual functions, v-tables, and VTTs #94056

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 10 commits into from
Jun 27, 2024
Merged
15 changes: 15 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/TypeSize.h"
#include <optional>
Expand Down Expand Up @@ -1277,6 +1278,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// space.
QualType removeAddrSpaceQualType(QualType T) const;

/// Return the "other" discriminator used for the pointer auth schema used for
/// vtable pointers in instances of the requested type.
uint16_t
getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD);

/// Apply Objective-C protocol qualifiers to the given type.
/// \param allowOnPointerType specifies if we can apply protocol
/// qualifiers on ObjCObjectPointerType. It can be set to true when
Expand Down Expand Up @@ -3438,12 +3444,21 @@ OPT_LIST(V)
/// Whether a C++ static variable or CUDA/HIP kernel should be externalized.
bool shouldExternalize(const Decl *D) const;

/// Resolve the root record to be used to derive the vtable pointer
/// authentication policy for the specified record.
const CXXRecordDecl *
baseForVTableAuthentication(const CXXRecordDecl *ThisClass);
bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
StringRef MangledName);

StringRef getCUIDHash() const;

private:
/// All OMPTraitInfo objects live in this collection, one per
/// `pragma omp [begin] declare variant` directive.
SmallVector<std::unique_ptr<OMPTraitInfo>, 4> OMPTraitInfoVector;

llvm::DenseMap<GlobalDecl, llvm::StringSet<>> ThunksToBeAbbreviated;
};

/// Insertion operator for diagnostics.
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/GlobalDecl.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ class GlobalDecl {
LHS.MultiVersionIndex == RHS.MultiVersionIndex;
}

bool operator!=(const GlobalDecl &Other) const {
return !(*this == Other);
}

void *getAsOpaquePtr() const { return Value.getOpaqueValue(); }

explicit operator bool() const { return getAsOpaquePtr(); }
Expand Down
11 changes: 5 additions & 6 deletions clang/include/clang/AST/Mangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,15 @@ class MangleContext {
// FIXME: consider replacing raw_ostream & with something like SmallString &.
void mangleName(GlobalDecl GD, raw_ostream &);
virtual void mangleCXXName(GlobalDecl GD, raw_ostream &) = 0;
virtual void mangleThunk(const CXXMethodDecl *MD,
const ThunkInfo &Thunk,
raw_ostream &) = 0;
virtual void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk,
bool ElideOverrideInfo, raw_ostream &) = 0;
virtual void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type,
const ThisAdjustment &ThisAdjustment,
raw_ostream &) = 0;
const ThunkInfo &Thunk,
bool ElideOverrideInfo, raw_ostream &) = 0;
virtual void mangleReferenceTemporary(const VarDecl *D,
unsigned ManglingNumber,
raw_ostream &) = 0;
virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0;
virtual void mangleCXXRTTI(QualType T, raw_ostream &) = 0;
virtual void mangleCXXRTTIName(QualType T, raw_ostream &,
bool NormalizeIntegers = false) = 0;
Expand Down Expand Up @@ -192,7 +192,6 @@ class ItaniumMangleContext : public MangleContext {
bool IsAux = false)
: MangleContext(C, D, MK_Itanium, IsAux) {}

virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0;
virtual void mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) = 0;
virtual void mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset,
const CXXRecordDecl *Type,
Expand Down
29 changes: 29 additions & 0 deletions clang/include/clang/AST/VTableBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ class VTableContextBase {
};

class ItaniumVTableContext : public VTableContextBase {
public:
typedef llvm::DenseMap<const CXXMethodDecl *, const CXXMethodDecl *>
OriginalMethodMapTy;

private:

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

/// Map from a virtual method to the nearest method in the primary base class
/// chain that it overrides.
OriginalMethodMapTy OriginalMethodMap;

void computeVTableRelatedInformation(const CXXRecordDecl *RD) override;

public:
Expand Down Expand Up @@ -425,6 +433,27 @@ class ItaniumVTableContext : public VTableContextBase {
CharUnits getVirtualBaseOffsetOffset(const CXXRecordDecl *RD,
const CXXRecordDecl *VBase);

/// Return the method that added the v-table slot that will be used to call
/// the given method.
///
/// In the Itanium ABI, where overrides always cause methods to be added to
/// the primary v-table if they're not already there, this will be the first
/// declaration in the primary base class chain for which the return type
/// adjustment is trivial.
GlobalDecl findOriginalMethod(GlobalDecl GD);

const CXXMethodDecl *findOriginalMethodInMap(const CXXMethodDecl *MD) const;

void setOriginalMethod(const CXXMethodDecl *Key, const CXXMethodDecl *Val) {
OriginalMethodMap[Key] = Val;
}

/// This method is reserved for the implementation and shouldn't be used
/// directly.
const OriginalMethodMapTy &getOriginalMethodMap() {
return OriginalMethodMap;
}

static bool classof(const VTableContextBase *VT) {
return !VT->isMicrosoft();
}
Expand Down
29 changes: 29 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,10 @@ class Attr {
bit PragmaAttributeSupport;
// Set to true if this attribute accepts parameter pack expansion expressions.
bit AcceptsExprPack = 0;
// To support multiple enum parameters to an attribute without breaking
// our existing general parsing we need to have a separate flag that
// opts an attribute into strict parsing of attribute parameters
bit StrictEnumParameters = 0;
// Lists language options, one of which is required to be true for the
// attribute to be applicable. If empty, no language options are required.
list<LangOpt> LangOpts = [];
Expand Down Expand Up @@ -4576,6 +4580,31 @@ def NoRandomizeLayout : InheritableAttr {
}
def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;

def VTablePointerAuthentication : InheritableAttr {
let Spellings = [Clang<"ptrauth_vtable_pointer">];
let Subjects = SubjectList<[CXXRecord]>;
let Documentation = [Undocumented];
let StrictEnumParameters = 1;
let Args = [EnumArgument<"Key", "VPtrAuthKeyType", /*is_string=*/ true,
["default_key", "no_authentication", "process_dependent",
"process_independent"],
["DefaultKey", "NoKey", "ProcessDependent",
"ProcessIndependent"]>,
EnumArgument<"AddressDiscrimination", "AddressDiscriminationMode",
/*is_string=*/ true,
["default_address_discrimination", "no_address_discrimination",
"address_discrimination"],
["DefaultAddressDiscrimination", "NoAddressDiscrimination",
"AddressDiscrimination"]>,
EnumArgument<"ExtraDiscrimination", "ExtraDiscrimination",
/*is_string=*/ true,
["default_extra_discrimination", "no_extra_discrimination",
"type_discrimination", "custom_discrimination"],
["DefaultExtraDiscrimination", "NoExtraDiscrimination",
"TypeDiscrimination", "CustomDiscrimination"]>,
IntArgument<"CustomDiscriminationValue", 1>];
}

def FunctionReturnThunks : InheritableAttr,
TargetSpecificAttr<TargetAnyX86> {
let Spellings = [GCC<"function_return">];
Expand Down
31 changes: 31 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,13 @@ def warn_ptrauth_auth_null_pointer :
def err_ptrauth_string_not_literal : Error<
"argument must be a string literal%select{| of char type}0">;

def note_ptrauth_virtual_function_pointer_incomplete_arg_ret :
Note<"cannot take an address of a virtual member function if its return or "
"argument types are incomplete">;
def note_ptrauth_virtual_function_incomplete_arg_ret_type :
Note<"%0 is incomplete">;


/// main()
// static main() is not an error in C, just in C++.
def warn_static_main : Warning<"'main' should not be declared static">,
Expand Down Expand Up @@ -12215,6 +12222,30 @@ def warn_cuda_maxclusterrank_sm_90 : Warning<
"maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring "
"%1 attribute">, InGroup<IgnoredAttributes>;

// VTable pointer authentication errors
def err_non_polymorphic_vtable_pointer_auth : Error<
"cannot set vtable pointer authentication on monomorphic type %0">;
def err_incomplete_type_vtable_pointer_auth : Error<
"cannot set vtable pointer authentication on an incomplete type %0">;
def err_non_top_level_vtable_pointer_auth : Error<
"cannot set vtable pointer authentication on %0 which is a subclass of polymorphic type %1">;
def err_duplicated_vtable_pointer_auth : Error<
"multiple vtable pointer authentication policies on %0">;
def err_invalid_authentication_key : Error<
"invalid authentication key %0">;
def err_invalid_address_discrimination : Error<
"invalid address discrimination mode %0">;
def err_invalid_extra_discrimination : Error<
"invalid extra discrimination selection %0">;
def err_invalid_custom_discrimination : Error<
"invalid custom discrimination">;
def err_missing_custom_discrimination : Error<
"missing custom discrimination">;
def err_no_default_vtable_pointer_auth : Error<
"cannot specify a default vtable pointer authentication "
"%select{key|address discrimination mode|discriminator}0 with no default set"
>;

def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must "
"have a bit size of at least %select{2|1}0">;
def err_bit_int_max_size : Error<"%select{signed|unsigned}0 _BitInt of bit "
Expand Down
25 changes: 25 additions & 0 deletions clang/include/clang/Basic/PointerAuthOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ class PointerAuthSchema {
/// No additional discrimination.
None,

/// Include a hash of the entity's type.
Type,

/// Include a hash of the entity's identity.
Decl,

/// Discriminate using a constant value.
Constant,
};
Expand Down Expand Up @@ -150,6 +156,25 @@ class PointerAuthSchema {
struct PointerAuthOptions {
/// The ABI for C function pointers.
PointerAuthSchema FunctionPointers;

/// The ABI for C++ virtual table pointers (the pointer to the table
/// itself) as installed in an actual class instance.
PointerAuthSchema CXXVTablePointers;

/// TypeInfo has external ABI requirements and is emitted without
/// actually having parsed the libcxx definition, so we can't simply
/// perform a look up. The settings for this should match the exact
/// specification in type_info.h
PointerAuthSchema CXXTypeInfoVTablePointer;

/// The ABI for C++ virtual table pointers as installed in a VTT.
PointerAuthSchema CXXVTTVTablePointers;

/// The ABI for most C++ virtual function pointers, i.e. v-table entries.
PointerAuthSchema CXXVirtualFunctionPointers;

/// The ABI for variadic C++ virtual function pointers.
PointerAuthSchema CXXVirtualVariadicFunctionPointers;
};

} // end namespace clang
Expand Down
14 changes: 9 additions & 5 deletions clang/include/clang/Basic/Thunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,20 +162,24 @@ struct ThunkInfo {

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

ThunkInfo() : Method(nullptr) {}
ThunkInfo() : Method(nullptr), ThisType(nullptr) {}

ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return,
const CXXMethodDecl *Method = nullptr)
: This(This), Return(Return), Method(Method) {}
const Type *ThisT, const CXXMethodDecl *Method = nullptr)
: This(This), Return(Return), Method(Method), ThisType(ThisT) {}

friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) {
return LHS.This == RHS.This && LHS.Return == RHS.Return &&
LHS.Method == RHS.Method;
LHS.Method == RHS.Method && LHS.ThisType == RHS.ThisType;
}

bool isEmpty() const {
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/CodeGen/CodeGenABITypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class CXXConstructorDecl;
class CXXDestructorDecl;
class CXXRecordDecl;
class CXXMethodDecl;
class GlobalDecl;
class ObjCMethodDecl;
class ObjCProtocolDecl;

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

/// Return a declaration discriminator for the given global decl.
uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD);

/// Given the language and code-generation options that Clang was configured
/// with, set the default LLVM IR attributes for a function definition.
/// The attributes set here are mostly global target-configuration and
Expand Down
10 changes: 9 additions & 1 deletion clang/include/clang/CodeGen/ConstantInitBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
#include <vector>

namespace clang {
namespace CodeGen {
class GlobalDecl;
class PointerAuthSchema;
class QualType;

namespace CodeGen {
class CodeGenModule;

/// A convenience builder class for complex constant initializers,
Expand Down Expand Up @@ -199,6 +202,11 @@ class ConstantAggregateBuilderBase {
add(llvm::ConstantInt::get(intTy, value, isSigned));
}

/// Add a signed pointer using the given pointer authentication schema.
void addSignedPointer(llvm::Constant *Pointer,
const PointerAuthSchema &Schema, GlobalDecl CalleeDecl,
QualType CalleeType);

/// Add a null pointer of a specific type.
void addNullPointer(llvm::PointerType *ptrTy) {
add(llvm::ConstantPointerNull::get(ptrTy));
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/InstallAPI/Visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class InstallAPIVisitor final : public ASTConsumer,
std::string getMangledName(const NamedDecl *D) const;
std::string getBackendMangledName(llvm::Twine Name) const;
std::string getMangledCXXVTableName(const CXXRecordDecl *D) const;
std::string getMangledCXXThunk(const GlobalDecl &D,
const ThunkInfo &Thunk) const;
std::string getMangledCXXThunk(const GlobalDecl &D, const ThunkInfo &Thunk,
bool ElideOverrideInfo) const;
std::string getMangledCXXRTTI(const CXXRecordDecl *D) const;
std::string getMangledCXXRTTIName(const CXXRecordDecl *D) const;
std::string getMangledCtorDtor(const CXXMethodDecl *D, int Type) const;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4566,6 +4566,10 @@ class Sema final : public SemaBase {
/// conditions that are needed for the attribute to have an effect.
void checkIllFormedTrivialABIStruct(CXXRecordDecl &RD);

/// Check that VTable Pointer authentication is only being set on the first
/// first instantiation of the vtable
void checkIncorrectVTablePointerAuthenticationAttribute(CXXRecordDecl &RD);

void ActOnFinishCXXMemberSpecification(Scope *S, SourceLocation RLoc,
Decl *TagDecl, SourceLocation LBrac,
SourceLocation RBrac,
Expand Down
Loading