diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index c4c23c835ebc2..cfbb9de4f2a06 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -4687,6 +4687,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; @@ -4695,14 +4698,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, @@ -4732,11 +4747,11 @@ class FunctionType : public Type { }; static ArmStateValue getArmZAState(unsigned AttrBits) { - return (ArmStateValue)((AttrBits & SME_ZAMask) >> SME_ZAShift); + return static_cast((AttrBits & SME_ZAMask) >> SME_ZAShift); } static ArmStateValue getArmZT0State(unsigned AttrBits) { - return (ArmStateValue)((AttrBits & SME_ZT0Mask) >> SME_ZT0Shift); + return static_cast((AttrBits & SME_ZT0Mask) >> SME_ZT0Shift); } /// A holder for Arm type attributes as described in the Arm C/C++ @@ -4745,6 +4760,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(AArch64SMETypeAttributes) unsigned AArch64SMEAttributes : 9; FunctionTypeArmAttributes() : AArch64SMEAttributes(SME_NormalFunction) {} @@ -5226,6 +5242,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> { @@ -5315,19 +5332,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(AArch64SMETypeAttributes) 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), @@ -5352,6 +5372,7 @@ class FunctionProtoType final bool requiresFunctionProtoTypeExtraBitfields() const { return ExceptionSpec.Type == EST_Dynamic || requiresFunctionProtoTypeArmAttributes() || + requiresFunctionProtoTypeExtraAttributeInfo() || !FunctionEffects.empty(); } @@ -5359,6 +5380,10 @@ class FunctionProtoType final return AArch64SMEAttributes != SME_NormalFunction; } + bool requiresFunctionProtoTypeExtraAttributeInfo() const { + return static_cast(ExtraAttributeInfo); + } + void setArmSMEAttribute(AArch64SMETypeAttributes Kind, bool Enable = true) { if (Enable) AArch64SMEAttributes |= Kind; @@ -5384,6 +5409,11 @@ class FunctionProtoType final return hasExtraBitfields(); } + unsigned + numTrailingObjects(OverloadToken) const { + return hasExtraAttributeInfo(); + } + unsigned numTrailingObjects(OverloadToken) const { return getExceptionSpecSize().NumExceptionType; } @@ -5476,6 +5506,12 @@ class FunctionProtoType final } + bool hasExtraAttributeInfo() const { + return FunctionTypeBits.HasExtraBitfields && + getTrailingObjects() + ->HasExtraAttributeInfo; + } + bool hasArmTypeAttributes() const { return FunctionTypeBits.HasExtraBitfields && getTrailingObjects() @@ -5509,6 +5545,7 @@ class FunctionProtoType final EPI.TypeQuals = getMethodQuals(); EPI.RefQualifier = getRefQualifier(); EPI.ExtParameterInfos = getExtParameterInfosOrNull(); + EPI.ExtraAttributeInfo = getExtraAttributeInfo(); EPI.AArch64SMEAttributes = getAArch64SMEAttributes(); EPI.FunctionEffects = getFunctionEffects(); return EPI; @@ -5696,6 +5733,13 @@ class FunctionProtoType final return getTrailingObjects(); } + /// Return the extra attribute information. + FunctionTypeExtraAttributeInfo getExtraAttributeInfo() const { + if (hasExtraAttributeInfo()) + return *getTrailingObjects(); + return FunctionTypeExtraAttributeInfo(); + } + /// Return a bitmask describing the SME attributes on the function type, see /// AArch64SMETypeAttributes for their values. unsigned getAArch64SMEAttributes() const { diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a9fa4a8f07454..8c8e0b3bca46c 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3922,6 +3922,14 @@ def CFICanonicalJumpTable : InheritableAttr { let SimpleHandler = 1; } +def CFISalt : TypeAttr { + let Spellings = [Clang<"cfi_salt">]; + let Args = [StringArgument<"Salt">]; + let Subjects = SubjectList<[FunctionLike], ErrorDiag>; + let Documentation = [CFISaltDocs]; + let LangOpts = [COnly]; +} + // 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 diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 2b095ab975202..00e8fc0787884 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3646,6 +3646,99 @@ make the function's CFI jump table canonical. See :ref:`the CFI documentation }]; } +def CFISaltDocs : Documentation { + let Category = DocCatFunction; + let Heading = "cfi_salt"; + let Label = "langext-cfi_salt"; + let Content = [{ +The ``cfi_salt`` attribute specifies a string literal that is used as a salt +for Control-Flow Integrity (CFI) checks to distinguish between functions with +the same type signature. This attribute can be applied to function declarations, +function definitions, and function pointer typedefs. + +The attribute prevents function pointers from being replaced with pointers to +functions that have a compatible type, which can be a CFI bypass vector. + +**Syntax:** + +* GNU-style: ``__attribute__((cfi_salt("")))`` +* C++11-style: ``[[clang::cfi_salt("")]]`` + +**Usage:** + +The attribute takes a single string literal argument that serves as the salt. +Functions or function types with different salt values will have different CFI +hashes, even if they have identical type signatures. + +**Motivation:** + +In large codebases like the Linux kernel, there are often hundreds of functions +with identical type signatures that are called indirectly: + +.. code-block:: + + 1662 functions with void (*)(void) + 1179 functions with int (*)(void) + ... + +By salting the CFI hashes, you can make CFI more robust by ensuring that +functions intended for different purposes have distinct CFI identities. + +**Type Compatibility:** + +* Functions with different salt values are considered to have incompatible types +* Function pointers with different salt values cannot be assigned to each other +* All declarations of the same function must use the same salt value + +**Example:** + +.. code-block:: c + + // Header file - define convenience macros + #define __cfi_salt(s) __attribute__((cfi_salt(s))) + + // Typedef for regular function pointers + typedef int (*fptr_t)(void); + + // Typedef for salted function pointers + typedef int (*fptr_salted_t)(void) __cfi_salt("pepper"); + + struct widget_ops { + fptr_t init; // Regular CFI + fptr_salted_t exec; // Salted CFI + fptr_t cleanup; // Regular CFI + }; + + // Function implementations + static int widget_init(void) { return 0; } + static int widget_exec(void) __cfi_salt("pepper") { return 1; } + static int widget_cleanup(void) { return 0; } + + static struct widget_ops ops = { + .init = widget_init, // OK - compatible types + .exec = widget_exec, // OK - both use "pepper" salt + .cleanup = widget_cleanup // OK - compatible types + }; + + // Using C++11 attribute syntax + void secure_callback(void) [[clang::cfi_salt("secure")]]; + + // This would cause a compilation error: + // fptr_t bad_ptr = widget_exec; // Error: incompatible types + +**Notes:** + +* The salt string can contain non-NULL ASCII characters, including spaces and + quotes +* This attribute only applies to function types; using it on non-function + types will generate a warning +* All declarations and definitions of the same function must use identical + salt values +* The attribute affects type compatibility during compilation and CFI hash + generation during code generation + }]; +} + def DocCatTypeSafety : DocumentationCategory<"Type Safety Checking"> { let Content = [{ Clang supports additional attributes to enable checking type safety properties diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 95dd42681d870..2f2685495a8f1 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5128,10 +5128,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, @@ -11552,6 +11554,11 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (lproto->getMethodQuals() != rproto->getMethodQuals()) return {}; + // Function protos with different 'cfi_salt' values aren't compatible. + if (lproto->getExtraAttributeInfo().CFISalt != + rproto->getExtraAttributeInfo().CFISalt) + return {}; + // Function effects are handled similarly to noreturn, see above. FunctionEffectsRef LHSFX = lproto->getFunctionEffects(); FunctionEffectsRef RHSFX = rproto->getFunctionEffects(); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c382e58cb07c4..f7949e94d227e 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3751,6 +3751,16 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, FunctionTypeBits.HasExtraBitfields = false; } + // Propagate any extra attribute information. + if (epi.requiresFunctionProtoTypeExtraAttributeInfo()) { + auto &ExtraAttrInfo = *getTrailingObjects(); + ExtraAttrInfo.CFISalt = epi.ExtraAttributeInfo.CFISalt; + + // Also set the bit in FunctionTypeExtraBitfields. + auto &ExtraBits = *getTrailingObjects(); + ExtraBits.HasExtraAttributeInfo = true; + } + if (epi.requiresFunctionProtoTypeArmAttributes()) { auto &ArmTypeAttrs = *getTrailingObjects(); ArmTypeAttrs = FunctionTypeArmAttributes(); @@ -3968,7 +3978,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 // @@ -4003,6 +4014,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(); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index ce5870e2da690..85242b69f0679 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2154,6 +2154,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::ExtVectorType: OS << "ext_vector_type"; break; + case attr::CFISalt: + OS << "cfi_salt(\"" << cast(T->getAttr())->getSalt() << "\")"; + break; } OS << "))"; } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index d077ee50856b7..652fe672f15e3 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2903,10 +2903,16 @@ void CodeGenFunction::EmitSanitizerStatReport(llvm::SanitizerStatKind SSK) { void CodeGenFunction::EmitKCFIOperandBundle( const CGCallee &Callee, SmallVectorImpl &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 * diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 2d37e0f13199b..414687640bf2d 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2366,7 +2366,7 @@ static QualType GeneralizeFunctionType(ASTContext &Ctx, QualType Ty) { llvm_unreachable("Encountered unknown FunctionType"); } -llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T) { +llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T, StringRef Salt) { if (getCodeGenOpts().SanitizeCfiICallGeneralizePointers) T = GeneralizeFunctionType(getContext(), T); if (auto *FnType = T->getAs()) @@ -2379,6 +2379,9 @@ llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T) { getCXXABI().getMangleContext().mangleCanonicalTypeName( T, Out, getCodeGenOpts().SanitizeCfiICallNormalizeIntegers); + if (!Salt.empty()) + Out << "." << Salt; + if (getCodeGenOpts().SanitizeCfiICallNormalizeIntegers) Out << ".normalized"; if (getCodeGenOpts().SanitizeCfiICallGeneralizePointers) @@ -3047,9 +3050,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()) + 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) { diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index cb013feb769fc..705d9a3cb9de3 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1621,7 +1621,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 diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 0985b5b565dab..d745cdbf0526f 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -156,6 +156,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr, case ParsedAttr::AT_Allocating: \ case ParsedAttr::AT_Regparm: \ case ParsedAttr::AT_CFIUncheckedCallee: \ + case ParsedAttr::AT_CFISalt: \ case ParsedAttr::AT_CmseNSCall: \ case ParsedAttr::AT_ArmStreaming: \ case ParsedAttr::AT_ArmStreamingCompatible: \ @@ -7986,6 +7987,36 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr, return true; } + if (attr.getKind() == ParsedAttr::AT_CFISalt) { + if (attr.getNumArgs() != 1) + return true; + + StringRef Argument; + if (!S.checkStringLiteralArgumentAttr(attr, 0, Argument)) + return true; + + // Delay if this is not a function type. + if (!unwrapped.isFunctionType()) + return false; + + const auto *FnTy = unwrapped.get()->getAs(); + if (!FnTy) { + S.Diag(attr.getLoc(), diag::err_attribute_wrong_decl_type) + << attr << attr.isRegularKeywordAttribute() + << ExpectedFunctionWithProtoType; + attr.setInvalid(); + return true; + } + + 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()); + return true; + } + if (attr.getKind() == ParsedAttr::AT_ArmStreaming || attr.getKind() == ParsedAttr::AT_ArmStreamingCompatible || attr.getKind() == ParsedAttr::AT_ArmPreserves || diff --git a/clang/test/CodeGen/cfi-salt.c b/clang/test/CodeGen/cfi-salt.c new file mode 100644 index 0000000000000..7ba1e2fc14daa --- /dev/null +++ b/clang/test/CodeGen/cfi-salt.c @@ -0,0 +1,188 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -DORIG_ATTR_SYN -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -fpatchable-function-entry-offset=3 -DORIG_ATTR_SYN -o - %s | FileCheck %s --check-prefixes=CHECK,OFFSET +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -fpatchable-function-entry-offset=3 -o - %s | FileCheck %s --check-prefixes=CHECK,OFFSET + +// Note that the interleving of functions, which normally would be in sequence, +// is due to the fact that Clang outputs them in a non-sequential order. + +#if !__has_feature(kcfi) +#error Missing kcfi? +#endif + +#ifdef ORIG_ATTR_SYN +#define __cfi_salt __attribute__((cfi_salt("pepper"))) +#define __cfi_salt_empty __attribute__((cfi_salt(""))) +#else +#define __cfi_salt [[clang::cfi_salt("pepper")]] +#define __cfi_salt_empty [[clang::cfi_salt("")]] +#endif + +typedef int (*fn_t)(void); +typedef int (* __cfi_salt fn_salt_t)(void); +typedef int (* __cfi_salt_empty fn_salt_empty_t)(void); + +typedef unsigned int (*ufn_t)(void); +typedef unsigned int (* __cfi_salt ufn_salt_t)(void); + +/// Must emit __kcfi_typeid symbols for address-taken function declarations +// CHECK: module asm ".weak __kcfi_typeid_[[F4:[a-zA-Z0-9_]+]]" +// CHECK: module asm ".set __kcfi_typeid_[[F4]], [[#%d,LOW_SODIUM_HASH:]]" +// CHECK: module asm ".weak __kcfi_typeid_[[F4_SALT:[a-zA-Z0-9_]+]]" +// CHECK: module asm ".set __kcfi_typeid_[[F4_SALT]], [[#%d,ASM_SALTY_HASH:]]" + +/// Must not __kcfi_typeid symbols for non-address-taken declarations +// CHECK-NOT: module asm ".weak __kcfi_typeid_f6" + +int f1(void); +int f1_salt(void) __cfi_salt; + +unsigned int f2(void); +unsigned int f2_salt(void) __cfi_salt; + +static int f3(void); +static int f3_salt(void) __cfi_salt; + +extern int f4(void); +extern int f4_salt(void) __cfi_salt; + +static int f5(void); +static int f5_salt(void) __cfi_salt; + +extern int f6(void); +extern int f6_salt(void) __cfi_salt; + +int f8(void); +int f8_salt_empty(void) __cfi_salt_empty; + +struct cfi_struct { + fn_t __cfi_salt fptr; + fn_salt_t td_fptr; + fn_salt_empty_t td_empty_fptr; +}; + +int f7_salt(struct cfi_struct *ptr); +int f7_typedef_salt(struct cfi_struct *ptr); + +// CHECK-LABEL: @__call +// CHECK: call{{.*}} i32 +// CHECK-NOT: "kcfi" +// CHECK-SAME: () +__attribute__((__no_sanitize__("kcfi"))) +int __call(fn_t f) { + return f(); +} + +// CHECK-LABEL: @call +// CHECK: call{{.*}} i32 %{{.}}(){{.*}} [ "kcfi"(i32 [[#LOW_SODIUM_HASH]]) ] +// CHECK-LABEL: @call_salt +// CHECK: call{{.*}} i32 %{{.}}(){{.*}} [ "kcfi"(i32 [[#%d,SALTY_HASH:]]) ] +// CHECK-LABEL: @call_salt_ty +// CHECK: call{{.*}} i32 %{{.}}(){{.*}} [ "kcfi"(i32 [[#SALTY_HASH]]) ] +int call(fn_t f) { return f(); } +int call_salt(fn_t __cfi_salt f) { return f(); } +int call_salt_ty(fn_salt_t f) { return f(); } +int call_salt_empty_ty(fn_salt_empty_t f) { return f(); } + +// CHECK-LABEL: @ucall +// CHECK: call{{.*}} i32 %{{.}}(){{.*}} [ "kcfi"(i32 [[#%d,LOW_SODIUM_UHASH:]]) ] +// CHECK-LABEL: @ucall_salt +// CHECK: call{{.*}} i32 %{{.}}(){{.*}} [ "kcfi"(i32 [[#%d,SALTY_UHASH:]]) ] +// CHECK-LABEL: @ucall_salt_ty +// CHECK: call{{.*}} i32 %{{.}}(){{.*}} [ "kcfi"(i32 [[#SALTY_UHASH]]) ] +unsigned int ucall(ufn_t f) { return f(); } +unsigned int ucall_salt(ufn_t __cfi_salt f) { return f(); } +unsigned int ucall_salt_ty(ufn_salt_t f) { return f(); } + +int test1(struct cfi_struct *ptr) { + return call(f1) + + call_salt(f1_salt) + + call_salt_ty(f1_salt) + + + __call((fn_t)f2) + + __call((fn_t)f2_salt) + + + ucall(f2) + + ucall_salt(f2_salt) + + ucall_salt_ty(f2_salt) + + + call(f3) + + call_salt(f3_salt) + + call_salt_ty(f3_salt) + + + call(f4) + + call_salt(f4_salt) + + call_salt_ty(f4_salt) + + + f5() + + f5_salt() + + + f6() + + f6_salt() + + + f7_salt(ptr) + + f7_typedef_salt(ptr) + + + f8() + + f8_salt_empty(); +} + +// CHECK-LABEL: define dso_local{{.*}} i32 @f1(){{.*}} !kcfi_type +// CHECK-SAME: ![[#LOW_SODIUM_TYPE:]] +// CHECK-LABEL: define dso_local{{.*}} i32 @f1_salt(){{.*}} !kcfi_type +// CHECK-SAME: ![[#SALTY_TYPE:]] +int f1(void) { return 0; } +int f1_salt(void) __cfi_salt { return 0; } + +// CHECK-LABEL: define dso_local{{.*}} i32 @f2(){{.*}} !kcfi_type +// CHECK-SAME: ![[#LOW_SODIUM_UTYPE:]] +// CHECK: define dso_local{{.*}} i32 @f2_salt(){{.*}} !kcfi_type +// CHECK-SAME: ![[#SALTY_UTYPE:]] +unsigned int f2(void) { return 2; } +unsigned int f2_salt(void) __cfi_salt { return 2; } + +// CHECK-LABEL: define internal{{.*}} i32 @f3(){{.*}} !kcfi_type +// CHECK-SAME: ![[#LOW_SODIUM_TYPE]] +// CHECK-LABEL: define internal{{.*}} i32 @f3_salt(){{.*}} !kcfi_type +// CHECK-SAME: ![[#SALTY_TYPE]] +static int f3(void) { return 1; } +static int f3_salt(void) __cfi_salt { return 1; } + +// CHECK: declare !kcfi_type ![[#LOW_SODIUM_TYPE]]{{.*}} i32 @[[F4]]() +// CHECK: declare !kcfi_type ![[#SALTY_TYPE]]{{.*}} i32 @[[F4_SALT]]() + +/// Must not emit !kcfi_type for non-address-taken local functions +// CHECK-LABEL: define internal{{.*}} i32 @f5() +// CHECK-NOT: !kcfi_type +// CHECK-SAME: { +// CHECK-LABEL: define internal{{.*}} i32 @f5_salt() +// CHECK-NOT: !kcfi_type +// CHECK-SAME: { +static int f5(void) { return 2; } +static int f5_salt(void) __cfi_salt { return 2; } + +// CHECK: declare !kcfi_type ![[#LOW_SODIUM_TYPE]]{{.*}} i32 @f6() +// CHECK: declare !kcfi_type ![[#SALTY_TYPE]]{{.*}} i32 @f6_salt() + +// CHECK-LABEL: @f7_salt +// CHECK: call{{.*}} i32 %{{.*}}() [ "kcfi"(i32 [[#SALTY_HASH]]) ] +// CHECK-LABEL: @f7_typedef_salt +// CHECK: call{{.*}} i32 %{{.*}}() [ "kcfi"(i32 [[#SALTY_HASH]]) ] +int f7_salt(struct cfi_struct *ptr) { return ptr->fptr(); } +int f7_typedef_salt(struct cfi_struct *ptr) { return ptr->td_fptr(); } + +// CHECK-LABEL: define dso_local{{.*}} i32 @f8(){{.*}} !kcfi_type +// CHECK-SAME: ![[#LOW_SODIUM_TYPE:]] +// CHECK-LABEL: define dso_local{{.*}} i32 @f8_salt_empty(){{.*}} !kcfi_type +// CHECK-SAME: ![[#LOW_SODIUM_TYPE:]] +int f8(void) { return 0; } +int f8_salt_empty(void) __cfi_salt_empty { return 0; } + +// CHECK: ![[#]] = !{i32 4, !"kcfi", i32 1} +// OFFSET: ![[#]] = !{i32 4, !"kcfi-offset", i32 3} +// +// CHECK: ![[#LOW_SODIUM_TYPE]] = !{i32 [[#LOW_SODIUM_HASH]]} +// CHECK: ![[#SALTY_TYPE]] = !{i32 [[#SALTY_HASH]]} +// +// CHECK: ![[#LOW_SODIUM_UTYPE]] = !{i32 [[#LOW_SODIUM_UHASH]]} +// CHECK: ![[#SALTY_UTYPE]] = !{i32 [[#SALTY_UHASH]]} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 05693538252aa..b9cf7cf9462fe 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -31,6 +31,7 @@ // CHECK-NEXT: CFConsumed (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: CFGuard (SubjectMatchRule_function) // CHECK-NEXT: CFICanonicalJumpTable (SubjectMatchRule_function) +// CHECK-NEXT: CFISalt (SubjectMatchRule_hasType_functionType) // CHECK-NEXT: CFUnknownTransfer (SubjectMatchRule_function) // CHECK-NEXT: CPUDispatch (SubjectMatchRule_function) // CHECK-NEXT: CPUSpecific (SubjectMatchRule_function) diff --git a/clang/test/Sema/attr-cfi-salt.c b/clang/test/Sema/attr-cfi-salt.c new file mode 100644 index 0000000000000..687f54dc499d8 --- /dev/null +++ b/clang/test/Sema/attr-cfi-salt.c @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -fsyntax-only -fsanitize=kcfi -verify %s +// RUN: %clang_cc1 -std=c89 -DKNR -fsyntax-only -fsanitize=kcfi -verify %s + +#define __cfi_salt(S) __attribute__((cfi_salt(S))) + +int bad1(void) __cfi_salt(); // expected-error{{'cfi_salt' attribute takes one argument}} +int bad2(void) __cfi_salt(42); // expected-error{{expected string literal as argument of 'cfi_salt' attribute}} +int bad3(void) __attribute__((cfi_salt("a", "b", "c"))); // expected-error{{'cfi_salt' attribute takes one argument}} + + +int foo(int a, int b) __cfi_salt("pepper"); // ok +int foo(int a, int b) __cfi_salt("pepper"); // ok + +#ifndef KNR +typedef int (*bar_t)(void) __cfi_salt("pepper"); // ok +typedef int (*bar_t)(void) __cfi_salt("pepper"); // ok +#endif + +// FIXME: Should we allow this? +// int b(void) __cfi_salt("salt 'n") __cfi_salt("pepper"); +// bar_t bar_fn __cfi_salt("salt 'n"); + +int baz __cfi_salt("salt"); // expected-warning{{'cfi_salt' only applies to function types}} + +int baz_fn(int a, int b) __cfi_salt("salt 'n"); // expected-note{{previous declaration is here}} +int baz_fn(int a, int b) __cfi_salt("pepper"); // expected-error{{conflicting types for 'baz_fn'}} + +int mux_fn(int a, int b) __cfi_salt("salt 'n"); // expected-note{{previous declaration is here}} +int mux_fn(int a, int b) __cfi_salt("pepper") { // expected-error{{conflicting types for 'mux_fn'}} + return a * b; +} + +typedef int qux_t __cfi_salt("salt"); // expected-warning{{'cfi_salt' only applies to function types}} + +typedef int (*quux_t)(void) __cfi_salt("salt 'n"); // expected-note{{previous definition is here}} +typedef int (*quux_t)(void) __cfi_salt("pepper"); // expected-error{{typedef redefinition with different type}} + +void func1(int a) __cfi_salt("pepper"); // expected-note{{previous declaration is here}} +void func1(int a) { } // expected-error{{conflicting types for 'func1'}} +void (*fp1)(int) = func1; // expected-error{{incompatible function pointer types initializing 'void (*)(int)' with an expression of type 'void (int)'}} + +void func2(int) [[clang::cfi_salt("test")]]; // expected-note{{previous declaration is here}} +void func2(int a) { } // expected-error{{conflicting types for 'func2'}} +void (*fp2)(int) = func2; // expected-error{{incompatible function pointer types initializing 'void (*)(int)' with an expression of type 'void (int)'}} + +void func3(int) __cfi_salt("pepper"); // ok +void func3(int a) __cfi_salt("pepper") { } // ok +void (* __cfi_salt("pepper") fp3)(int) = func3; // ok +void (*fp3_noattr)(int) = func3; // expected-error{{incompatible function pointer types initializing 'void (*)(int)' with an expression of type 'void (int)'}} + +void func4(int) [[clang::cfi_salt("test")]]; // ok +void func4(int a) [[clang::cfi_salt("test")]] { } // ok +void (* [[clang::cfi_salt("test")]] fp4)(int) = func4; // ok +void (*fp4_noattr)(int) = func4; // expected-error{{incompatible function pointer types initializing 'void (*)(int)' with an expression of type 'void (int)'}} + +#ifdef KNR +// K&R C function without a prototype +void func() __attribute__((cfi_salt("pepper"))); // expected-error {{attribute only applies to non-K&R-style functions}} +void (*fp)() __attribute__((cfi_salt("pepper"))); // expected-error {{attribute only applies to non-K&R-style functions}} +#endif