Skip to content

[Clang][attr] Add cfi_salt attribute #141846

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 55 additions & 11 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -4631,6 +4631,9 @@ class FunctionType : public Type {
/// [implimits] 8 bits would be enough here.
unsigned NumExceptionType : 10;

LLVM_PREFERRED_TYPE(bool)
unsigned HasExtraAttributeInfo : 1;

LLVM_PREFERRED_TYPE(bool)
unsigned HasArmTypeAttributes : 1;

Expand All @@ -4639,14 +4642,26 @@ class FunctionType : public Type {
unsigned NumFunctionEffects : 4;

FunctionTypeExtraBitfields()
: NumExceptionType(0), HasArmTypeAttributes(false),
EffectsHaveConditions(false), NumFunctionEffects(0) {}
: NumExceptionType(0), HasExtraAttributeInfo(false),
HasArmTypeAttributes(false), EffectsHaveConditions(false),
NumFunctionEffects(0) {}
};

/// A holder for extra information from attributes which aren't part of an
/// \p AttributedType.
struct alignas(void *) FunctionTypeExtraAttributeInfo {
/// A CFI "salt" that differentiates functions with the same prototype.
StringRef CFISalt;

operator bool() const { return !CFISalt.empty(); }

void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddString(CFISalt); }
};

/// The AArch64 SME ACLE (Arm C/C++ Language Extensions) define a number
/// of function type attributes that can be set on function types, including
/// function pointers.
enum AArch64SMETypeAttributes : unsigned {
enum AArch64SMETypeAttributes : uint16_t {
SME_NormalFunction = 0,
SME_PStateSMEnabledMask = 1 << 0,
SME_PStateSMCompatibleMask = 1 << 1,
Expand Down Expand Up @@ -4676,11 +4691,11 @@ class FunctionType : public Type {
};

static ArmStateValue getArmZAState(unsigned AttrBits) {
return (ArmStateValue)((AttrBits & SME_ZAMask) >> SME_ZAShift);
return static_cast<ArmStateValue>((AttrBits & SME_ZAMask) >> SME_ZAShift);
}

static ArmStateValue getArmZT0State(unsigned AttrBits) {
return (ArmStateValue)((AttrBits & SME_ZT0Mask) >> SME_ZT0Shift);
return static_cast<ArmStateValue>((AttrBits & SME_ZT0Mask) >> SME_ZT0Shift);
}

/// A holder for Arm type attributes as described in the Arm C/C++
Expand All @@ -4689,6 +4704,7 @@ class FunctionType : public Type {
struct alignas(void *) FunctionTypeArmAttributes {
/// Any AArch64 SME ACLE type attributes that need to be propagated
/// on declarations and function pointers.
LLVM_PREFERRED_TYPE(uint16_t)
unsigned AArch64SMEAttributes : 9;

FunctionTypeArmAttributes() : AArch64SMEAttributes(SME_NormalFunction) {}
Expand Down Expand Up @@ -5170,6 +5186,7 @@ class FunctionProtoType final
private llvm::TrailingObjects<
FunctionProtoType, QualType, SourceLocation,
FunctionType::FunctionTypeExtraBitfields,
FunctionType::FunctionTypeExtraAttributeInfo,
FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType,
Expr *, FunctionDecl *, FunctionType::ExtParameterInfo, Qualifiers,
FunctionEffect, EffectConditionExpr> {
Expand Down Expand Up @@ -5259,19 +5276,22 @@ class FunctionProtoType final
/// the various bits of extra information about a function prototype.
struct ExtProtoInfo {
FunctionType::ExtInfo ExtInfo;
Qualifiers TypeQuals;
RefQualifierKind RefQualifier = RQ_None;
ExceptionSpecInfo ExceptionSpec;
const ExtParameterInfo *ExtParameterInfos = nullptr;
SourceLocation EllipsisLoc;
FunctionEffectsRef FunctionEffects;
FunctionTypeExtraAttributeInfo ExtraAttributeInfo;

LLVM_PREFERRED_TYPE(bool)
unsigned Variadic : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned HasTrailingReturn : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned CFIUncheckedCallee : 1;
LLVM_PREFERRED_TYPE(uint16_t)
unsigned AArch64SMEAttributes : 9;
Qualifiers TypeQuals;
RefQualifierKind RefQualifier = RQ_None;
ExceptionSpecInfo ExceptionSpec;
const ExtParameterInfo *ExtParameterInfos = nullptr;
SourceLocation EllipsisLoc;
FunctionEffectsRef FunctionEffects;

ExtProtoInfo()
: Variadic(false), HasTrailingReturn(false), CFIUncheckedCallee(false),
Expand All @@ -5296,13 +5316,18 @@ class FunctionProtoType final
bool requiresFunctionProtoTypeExtraBitfields() const {
return ExceptionSpec.Type == EST_Dynamic ||
requiresFunctionProtoTypeArmAttributes() ||
requiresFunctionProtoTypeExtraAttributeInfo() ||
!FunctionEffects.empty();
}

bool requiresFunctionProtoTypeArmAttributes() const {
return AArch64SMEAttributes != SME_NormalFunction;
}

bool requiresFunctionProtoTypeExtraAttributeInfo() const {
return static_cast<bool>(ExtraAttributeInfo);
}

void setArmSMEAttribute(AArch64SMETypeAttributes Kind, bool Enable = true) {
if (Enable)
AArch64SMEAttributes |= Kind;
Expand All @@ -5328,6 +5353,11 @@ class FunctionProtoType final
return hasExtraBitfields();
}

unsigned
numTrailingObjects(OverloadToken<FunctionTypeExtraAttributeInfo>) const {
return hasExtraAttributeInfo();
}

unsigned numTrailingObjects(OverloadToken<ExceptionType>) const {
return getExceptionSpecSize().NumExceptionType;
}
Expand Down Expand Up @@ -5424,6 +5454,12 @@ class FunctionProtoType final

}

bool hasExtraAttributeInfo() const {
return FunctionTypeBits.HasExtraBitfields &&
getTrailingObjects<FunctionTypeExtraBitfields>()
->HasExtraAttributeInfo;
}

bool hasArmTypeAttributes() const {
return FunctionTypeBits.HasExtraBitfields &&
getTrailingObjects<FunctionTypeExtraBitfields>()
Expand Down Expand Up @@ -5457,6 +5493,7 @@ class FunctionProtoType final
EPI.TypeQuals = getMethodQuals();
EPI.RefQualifier = getRefQualifier();
EPI.ExtParameterInfos = getExtParameterInfosOrNull();
EPI.ExtraAttributeInfo = getExtraAttributeInfo();
EPI.AArch64SMEAttributes = getAArch64SMEAttributes();
EPI.FunctionEffects = getFunctionEffects();
return EPI;
Expand Down Expand Up @@ -5644,6 +5681,13 @@ class FunctionProtoType final
return getTrailingObjects<ExtParameterInfo>();
}

/// Return the extra attribute information.
FunctionTypeExtraAttributeInfo getExtraAttributeInfo() const {
if (hasExtraAttributeInfo())
return *getTrailingObjects<FunctionTypeExtraAttributeInfo>();
return FunctionTypeExtraAttributeInfo();
}

/// Return a bitmask describing the SME attributes on the function type, see
/// AArch64SMETypeAttributes for their values.
unsigned getAArch64SMEAttributes() const {
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -3843,6 +3843,13 @@ def CFICanonicalJumpTable : InheritableAttr {
let SimpleHandler = 1;
}

def CFISalt : DeclOrTypeAttr {
let Spellings = [Clang<"cfi_salt">];
let Args = [StringArgument<"Salt">];
let Subjects = SubjectList<[Function, Field, Var, TypedefName], ErrorDiag>;
let Documentation = [CFISaltDocs];
}

// C/C++ Thread safety attributes (e.g. for deadlock, data race checking)
// Not all of these attributes will be given a [[]] spelling. The attributes
// which require access to function parameter names cannot use the [[]] spelling
Expand Down
58 changes: 58 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -3639,6 +3639,64 @@ make the function's CFI jump table canonical. See :ref:`the CFI documentation
}];
}

def CFISaltDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Use ``__attribute__((cfi_salt("<salt>")))`` on a function declaration, function
definition, or typedef to help distinguish CFI hashes between functions with
the same type signature.

Example use:

.. code-block:: c

// .h file:
#define __cfi_salt __attribute__((cfi_salt("vogon")))

// Convenient typedefs to avoid nested declarator syntax.
typedef int (*fptr_t)(void); // Non-salted function call.
typedef int (*fptr_salted_t)(void) __cfi_salt;

struct widget_generator {
fptr_t init;
fptr_salted_t exec;
fptr_t teardown;
};

// 1st .c file:
static int internal_init(void) { /* ... */ }
static int internal_salted_exec(void) __cfi_salt { /* ... */ }
static int internal_teardown(void) { /* ... */ }

static struct widget_generator _generator = {
.init = internal_init,
.exec = internal_salted_exec,
.teardown = internal_teardown,
}

struct widget_generator *widget_gen = _generator;

// 2nd .c file:
int generate_a_widget(void) {
int ret;

// Called with non-salted CFI.
ret = widget_gen.init();
if (ret)
return ret;

// Called with salted CFI.
ret = widget_gen.exec();
if (ret)
return ret;

// Called with non-salted CFI.
return widget_gen.teardown();
}

}];
}

def DocCatTypeSafety : DocumentationCategory<"Type Safety Checking"> {
let Content = [{
Clang supports additional attributes to enable checking type safety properties
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5055,10 +5055,12 @@ QualType ASTContext::getFunctionTypeInternal(
EPI.ExceptionSpec.Type, EPI.ExceptionSpec.Exceptions.size());
size_t Size = FunctionProtoType::totalSizeToAlloc<
QualType, SourceLocation, FunctionType::FunctionTypeExtraBitfields,
FunctionType::FunctionTypeExtraAttributeInfo,
FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType,
Expr *, FunctionDecl *, FunctionProtoType::ExtParameterInfo, Qualifiers,
FunctionEffect, EffectConditionExpr>(
NumArgs, EPI.Variadic, EPI.requiresFunctionProtoTypeExtraBitfields(),
EPI.requiresFunctionProtoTypeExtraAttributeInfo(),
EPI.requiresFunctionProtoTypeArmAttributes(), ESH.NumExceptionType,
ESH.NumExprPtr, ESH.NumFunctionDeclPtr,
EPI.ExtParameterInfos ? NumArgs : 0,
Expand Down
14 changes: 13 additions & 1 deletion clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3679,6 +3679,16 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef<QualType> params,
FunctionTypeBits.HasExtraBitfields = false;
}

// Propagate any extra attribute information.
if (epi.requiresFunctionProtoTypeExtraAttributeInfo()) {
auto &ExtraAttrInfo = *getTrailingObjects<FunctionTypeExtraAttributeInfo>();
ExtraAttrInfo.CFISalt = epi.ExtraAttributeInfo.CFISalt;

// Also set the bit in FunctionTypeExtraBitfields.
auto &ExtraBits = *getTrailingObjects<FunctionTypeExtraBitfields>();
ExtraBits.HasExtraAttributeInfo = true;
}

if (epi.requiresFunctionProtoTypeArmAttributes()) {
auto &ArmTypeAttrs = *getTrailingObjects<FunctionTypeArmAttributes>();
ArmTypeAttrs = FunctionTypeArmAttributes();
Expand Down Expand Up @@ -3896,7 +3906,8 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
// This is followed by the ext info:
// int
// Finally we have a trailing return type flag (bool)
// combined with AArch64 SME Attributes, to save space:
// combined with AArch64 SME Attributes and extra attribute info, to save
// space:
// int
// combined with any FunctionEffects
//
Expand Down Expand Up @@ -3931,6 +3942,7 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
}

epi.ExtInfo.Profile(ID);
epi.ExtraAttributeInfo.Profile(ID);

unsigned EffectCount = epi.FunctionEffects.size();
bool HasConds = !epi.FunctionEffects.Conditions.empty();
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2105,6 +2105,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::ExtVectorType:
OS << "ext_vector_type";
break;
case attr::CFISalt:
OS << "cfi_salt(\"" << cast<CFISaltAttr>(T->getAttr())->getSalt() << "\")";
break;
}
OS << "))";
}
Expand Down
14 changes: 10 additions & 4 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2896,10 +2896,16 @@ void CodeGenFunction::EmitSanitizerStatReport(llvm::SanitizerStatKind SSK) {

void CodeGenFunction::EmitKCFIOperandBundle(
const CGCallee &Callee, SmallVectorImpl<llvm::OperandBundleDef> &Bundles) {
const FunctionProtoType *FP =
Callee.getAbstractInfo().getCalleeFunctionProtoType();
if (FP)
Bundles.emplace_back("kcfi", CGM.CreateKCFITypeId(FP->desugar()));
const CGCalleeInfo &CI = Callee.getAbstractInfo();
const FunctionProtoType *FP = CI.getCalleeFunctionProtoType();
if (!FP)
return;

StringRef Salt;
if (const auto &Info = FP->getExtraAttributeInfo())
Salt = Info.CFISalt;

Bundles.emplace_back("kcfi", CGM.CreateKCFITypeId(FP->desugar(), Salt));
}

llvm::Value *
Expand Down
15 changes: 12 additions & 3 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2221,7 +2221,7 @@ llvm::ConstantInt *CodeGenModule::CreateCrossDsoCfiTypeId(llvm::Metadata *MD) {
return llvm::ConstantInt::get(Int64Ty, llvm::MD5Hash(MDS->getString()));
}

llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T) {
llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T, StringRef Salt) {
if (auto *FnType = T->getAs<FunctionProtoType>())
T = getContext().getFunctionType(
FnType->getReturnType(), FnType->getParamTypes(),
Expand All @@ -2232,6 +2232,9 @@ llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T) {
getCXXABI().getMangleContext().mangleCanonicalTypeName(
T, Out, getCodeGenOpts().SanitizeCfiICallNormalizeIntegers);

if (!Salt.empty())
Out << "." << Salt;

if (getCodeGenOpts().SanitizeCfiICallNormalizeIntegers)
Out << ".normalized";

Expand Down Expand Up @@ -2898,9 +2901,15 @@ void CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
void CodeGenModule::setKCFIType(const FunctionDecl *FD, llvm::Function *F) {
llvm::LLVMContext &Ctx = F->getContext();
llvm::MDBuilder MDB(Ctx);
llvm::StringRef Salt;

if (const auto *FP = FD->getType()->getAs<FunctionProtoType>())
if (const auto &Info = FP->getExtraAttributeInfo())
Salt = Info.CFISalt;

F->setMetadata(llvm::LLVMContext::MD_kcfi_type,
llvm::MDNode::get(
Ctx, MDB.createConstant(CreateKCFITypeId(FD->getType()))));
llvm::MDNode::get(Ctx, MDB.createConstant(CreateKCFITypeId(
FD->getType(), Salt))));
}

static bool allowKCFIIdentifier(StringRef Name) {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1616,7 +1616,7 @@ class CodeGenModule : public CodeGenTypeCache {
llvm::ConstantInt *CreateCrossDsoCfiTypeId(llvm::Metadata *MD);

/// Generate a KCFI type identifier for T.
llvm::ConstantInt *CreateKCFITypeId(QualType T);
llvm::ConstantInt *CreateKCFITypeId(QualType T, StringRef Salt);

/// Create a metadata identifier for the given type. This may either be an
/// MDString (for external identifiers) or a distinct unnamed MDNode (for
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
case ParsedAttr::AT_ArmOut: \
case ParsedAttr::AT_ArmInOut: \
case ParsedAttr::AT_ArmAgnostic: \
case ParsedAttr::AT_CFISalt: \
case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \
case ParsedAttr::AT_AnyX86NoCfCheck: \
CALLING_CONV_ATTRS_CASELIST
Expand Down Expand Up @@ -7943,6 +7944,21 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
return true;
}

if (attr.getKind() == ParsedAttr::AT_CFISalt) {
StringRef Argument;
if (!S.checkStringLiteralArgumentAttr(attr, 0, Argument))
return false;

const auto *FnTy = unwrapped.get()->getAs<FunctionProtoType>();
FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo();
EPI.ExtraAttributeInfo.CFISalt = Argument;

QualType newtype = S.Context.getFunctionType(FnTy->getReturnType(),
FnTy->getParamTypes(), EPI);
type = unwrapped.wrap(S, newtype->getAs<FunctionType>());
return true;
}

if (attr.getKind() == ParsedAttr::AT_ArmStreaming ||
attr.getKind() == ParsedAttr::AT_ArmStreamingCompatible ||
attr.getKind() == ParsedAttr::AT_ArmPreserves ||
Expand Down
Loading
Loading