Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -1446,7 +1446,7 @@ /// Extra information which affects how the function is called, like /// regparm and the calling convention. - unsigned ExtInfo : 11; + unsigned ExtInfo : 12; /// Used only by FunctionProtoType, put here to pack with the /// other bitfields. @@ -3081,24 +3081,24 @@ class ExtInfo { friend class FunctionType; - // Feel free to rearrange or add bits, but if you go over 11, + // Feel free to rearrange or add bits, but if you go over 12, // you'll need to adjust both the Bits field below and // Type::FunctionTypeBitfields. - // | CC |noreturn|produces|nocallersavedregs|regparm| - // |0 .. 4| 5 | 6 | 7 |8 .. 10| + // | CC |noreturn|produces|nocallersavedregs|regparm|nocfcheck| + // |0 .. 4| 5 | 6 | 7 |8 .. 10| 11 | // // regparm is either 0 (no regparm attribute) or the regparm value+1. enum { CallConvMask = 0x1F }; enum { NoReturnMask = 0x20 }; enum { ProducesResultMask = 0x40 }; enum { NoCallerSavedRegsMask = 0x80 }; + enum { NoCfCheckMask = 0x800 }; enum { RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask | - NoCallerSavedRegsMask), + NoCallerSavedRegsMask | NoCfCheckMask), RegParmOffset = 8 }; // Assumed to be the last field - uint16_t Bits = CC_C; ExtInfo(unsigned Bits) : Bits(static_cast(Bits)) {} @@ -3107,12 +3107,13 @@ // Constructor with no defaults. Use this when you know that you // have all the elements (when reading an AST file for example). ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc, - bool producesResult, bool noCallerSavedRegs) { + bool producesResult, bool noCallerSavedRegs, bool NoCfCheck) { assert((!hasRegParm || regParm < 7) && "Invalid regparm value"); Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) | (producesResult ? ProducesResultMask : 0) | (noCallerSavedRegs ? NoCallerSavedRegsMask : 0) | - (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0); + (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) | + (NoCfCheck ? NoCfCheckMask : 0); } // Constructor with all defaults. Use when for example creating a @@ -3126,10 +3127,11 @@ bool getNoReturn() const { return Bits & NoReturnMask; } bool getProducesResult() const { return Bits & ProducesResultMask; } bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; } + bool getNoCfCheck() const { return Bits & NoCfCheckMask; } bool getHasRegParm() const { return (Bits >> RegParmOffset) != 0; } unsigned getRegParm() const { - unsigned RegParm = Bits >> RegParmOffset; + unsigned RegParm = (Bits & RegParmMask) >> RegParmOffset; if (RegParm > 0) --RegParm; return RegParm; @@ -3168,6 +3170,13 @@ return ExtInfo(Bits & ~NoCallerSavedRegsMask); } + ExtInfo withNoCfCheck(bool noCfCheck) const { + if (noCfCheck) + return ExtInfo(Bits | NoCfCheckMask); + else + return ExtInfo(Bits & ~NoCfCheckMask); + } + ExtInfo withRegParm(unsigned RegParm) const { assert(RegParm < 7 && "Invalid regparm value"); return ExtInfo((Bits & ~RegParmMask) | @@ -4054,6 +4063,7 @@ // No operand. attr_noreturn, + attr_nocf_check, attr_cdecl, attr_fastcall, attr_stdcall, Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -2085,6 +2085,12 @@ let Documentation = [AnyX86NoCallerSavedRegistersDocs]; } +def AnyX86NoCfCheck : InheritableAttr, TargetSpecificAttr{ + let Spellings = [GCC<"nocf_check">]; + let Subjects = SubjectList<[FunctionLike]>; + let Documentation = [AnyX86NoCfCheckDocs]; +} + def X86ForceAlignArgPointer : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"force_align_arg_pointer">]; // Technically, this appertains to a FunctionDecl, but the target-specific Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -2870,6 +2870,24 @@ }]; } +def AnyX86NoCfCheckDocs : Documentation{ + let Category = DocCatFunction; + let Content = [{ +Jump Oriented Programming attacks rely on tampering with addresses used by +indirect call / jmp, e.g. redirect control-flow to non-programmer +intended bytes in the binary. +X86 Supports Indirect Branch Tracking (IBT) as part of Control-Flow +Enforcement Technology (CET). IBT instruments ENDBR instructions used to +specify valid targets of indirect call / jmp. +The ``nocf_check`` attribute has two roles: +1. Appertains to a function - do not add ENDBR instruction at the + beginning of the function. +2. Appertains to a function pointer - do not track the target + function of this pointer (by adding nocf_check prefix to the + indirect-call instruction). +}]; +} + def SwiftCallDocs : Documentation { let Category = DocCatVariable; let Content = [{ Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2687,6 +2687,9 @@ def warn_attribute_ignored_on_inline : Warning<"%0 attribute ignored on inline function">, InGroup; +def warn_nocf_check_attribute_ignored : + Warning<"'nocf_check' attribute ignored; use -fcf-protection to enable the attribute">, + InGroup; def warn_attribute_after_definition_ignored : Warning< "attribute %0 after definition is ignored">, InGroup; Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -234,6 +234,7 @@ LANGOPT(ObjCWeakRuntime , 1, 0, "__weak support in the ARC runtime") LANGOPT(ObjCWeak , 1, 0, "Objective-C __weak in ARC and MRC files") LANGOPT(ObjCSubscriptingLegacyRuntime , 1, 0, "Subscripting support in legacy ObjectiveC runtime") +LANGOPT(CFProtectionBranch , 1, 0, "Control-Flow Branch Protection enabled") LANGOPT(FakeAddressSpaceMap , 1, 0, "OpenCL fake address space map") ENUM_LANGOPT(AddressSpaceMapMangling , AddrSpaceMapMangling, 2, ASMM_Target, "OpenCL address space map mangling mode") LANGOPT(IncludeDefaultHeader, 1, 0, "Include default header file for OpenCL") Index: include/clang/CodeGen/CGFunctionInfo.h =================================================================== --- include/clang/CodeGen/CGFunctionInfo.h +++ include/clang/CodeGen/CGFunctionInfo.h @@ -515,6 +515,9 @@ unsigned HasRegParm : 1; unsigned RegParm : 3; + /// Whether this function has nocf_check attribute. + unsigned NoCfCheck : 1; + RequiredArgs Required; /// The struct representing all arguments passed in memory. Only used when @@ -599,6 +602,9 @@ /// Whether this function no longer saves caller registers. bool isNoCallerSavedRegs() const { return NoCallerSavedRegs; } + /// Whether this function has nocf_check attribute. + bool isNoCfCheck() const { return NoCfCheck; } + /// getASTCallingConvention() - Return the AST-specified calling /// convention. CallingConv getASTCallingConvention() const { @@ -624,7 +630,7 @@ FunctionType::ExtInfo getExtInfo() const { return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(), getASTCallingConvention(), isReturnsRetained(), - isNoCallerSavedRegs()); + isNoCallerSavedRegs(), isNoCfCheck()); } CanQualType getReturnType() const { return getArgsBuffer()[0].type; } @@ -664,6 +670,7 @@ ID.AddBoolean(NoCallerSavedRegs); ID.AddBoolean(HasRegParm); ID.AddInteger(RegParm); + ID.AddBoolean(NoCfCheck); ID.AddInteger(Required.getOpaqueData()); ID.AddBoolean(HasExtParameterInfos); if (HasExtParameterInfos) { @@ -690,6 +697,7 @@ ID.AddBoolean(info.getNoCallerSavedRegs()); ID.AddBoolean(info.getHasRegParm()); ID.AddInteger(info.getRegParm()); + ID.AddBoolean(info.getNoCfCheck()); ID.AddInteger(required.getOpaqueData()); ID.AddBoolean(!paramInfos.empty()); if (!paramInfos.empty()) { Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3328,8 +3328,8 @@ bool CheckRegparmAttr(const AttributeList &attr, unsigned &value); bool CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, const FunctionDecl *FD = nullptr); - bool CheckNoReturnAttr(const AttributeList &attr); - bool CheckNoCallerSavedRegsAttr(const AttributeList &attr); + bool CheckAttrTarget(const AttributeList &CurrAttr); + bool CheckAttrNoArgs(const AttributeList &CurrAttr); bool checkStringLiteralArgumentAttr(const AttributeList &Attr, unsigned ArgNum, StringRef &Str, SourceLocation *ArgLocation = nullptr); Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -8229,6 +8229,8 @@ return QualType(); if (lbaseInfo.getNoCallerSavedRegs() != rbaseInfo.getNoCallerSavedRegs()) return QualType(); + if (lbaseInfo.getNoCfCheck() != rbaseInfo.getNoCfCheck()) + return QualType(); // FIXME: some uses, e.g. conditional exprs, really want this to be 'both'. bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn(); Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -3097,6 +3097,7 @@ case AttributedType::attr_uptr: case AttributedType::attr_objc_kindof: case AttributedType::attr_ns_returns_retained: + case AttributedType::attr_nocf_check: return false; } llvm_unreachable("bad attributed type kind"); @@ -3134,6 +3135,7 @@ case attr_nullable: case attr_null_unspecified: case attr_objc_kindof: + case attr_nocf_check: return false; case attr_pcs: Index: lib/AST/TypePrinter.cpp =================================================================== --- lib/AST/TypePrinter.cpp +++ lib/AST/TypePrinter.cpp @@ -801,6 +801,8 @@ << Info.getRegParm() << ")))"; if (Info.getNoCallerSavedRegs()) OS << " __attribute__((no_caller_saved_registers))"; + if (Info.getNoCfCheck()) + OS << " __attribute__((nocf_check))"; } void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T, @@ -1396,7 +1398,7 @@ // FIXME: When Sema learns to form this AttributedType, avoid printing the // attribute again in printFunctionProtoAfter. case AttributedType::attr_noreturn: OS << "noreturn"; break; - + case AttributedType::attr_nocf_check: OS << "nocf_check"; break; case AttributedType::attr_cdecl: OS << "cdecl"; break; case AttributedType::attr_fastcall: OS << "fastcall"; break; case AttributedType::attr_stdcall: OS << "stdcall"; break; Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -803,6 +803,7 @@ FI->NoReturn = info.getNoReturn(); FI->ReturnsRetained = info.getProducesResult(); FI->NoCallerSavedRegs = info.getNoCallerSavedRegs(); + FI->NoCfCheck = info.getNoCfCheck(); FI->Required = required; FI->HasRegParm = info.getHasRegParm(); FI->RegParm = info.getRegParm(); @@ -1842,6 +1843,8 @@ RetAttrs.addAttribute(llvm::Attribute::NonNull); if (TargetDecl->hasAttr()) FuncAttrs.addAttribute("no_caller_saved_registers"); + if (TargetDecl->hasAttr()) + FuncAttrs.addAttribute(llvm::Attribute::NoCfCheck); HasOptnone = TargetDecl->hasAttr(); if (auto *AllocSize = TargetDecl->getAttr()) { Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1994,6 +1994,12 @@ } } + if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) { + StringRef Name = A->getValue(); + if (Name == "full" || Name == "branch") { + Opts.CFProtectionBranch = 1; + } + } // -cl-std only applies for OpenCL language standards. // Override the -std option in this case. if (const Arg *A = Args.getLastArg(OPT_cl_std_EQ)) { Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1965,9 +1965,6 @@ static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &Attrs) { if (hasDeclarator(D)) return; - if (S.CheckNoReturnAttr(Attrs)) - return; - if (!isa(D)) { S.Diag(Attrs.getLoc(), diag::warn_attribute_wrong_decl_type) << Attrs.getName() << ExpectedFunctionOrMethod; @@ -1978,25 +1975,23 @@ Attrs.getRange(), S.Context, Attrs.getAttributeSpellingListIndex())); } -static void handleNoCallerSavedRegsAttr(Sema &S, Decl *D, - const AttributeList &Attr) { - if (S.CheckNoCallerSavedRegsAttr(Attr)) - return; - - D->addAttr(::new (S.Context) AnyX86NoCallerSavedRegistersAttr( - Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); +static void handleNoCfCheckAttr(Sema &S, Decl *D, const AttributeList &CurrAttr) { + if (!S.getLangOpts().CFProtectionBranch) + S.Diag(CurrAttr.getLoc(), diag::warn_nocf_check_attribute_ignored); + else + handleSimpleAttribute(S, D, CurrAttr); } -bool Sema::CheckNoReturnAttr(const AttributeList &Attrs) { - if (!checkAttributeNumArgs(*this, Attrs, 0)) { - Attrs.setInvalid(); +bool Sema::CheckAttrNoArgs(const AttributeList &CurrAttr) { + if (!checkAttributeNumArgs(*this, CurrAttr, 0)) { + CurrAttr.setInvalid(); return true; } return false; } -bool Sema::CheckNoCallerSavedRegsAttr(const AttributeList &Attr) { +bool Sema::CheckAttrTarget(const AttributeList &Attr) { // Check whether the attribute is valid on the current target. if (!Attr.existsInTarget(Context.getTargetInfo())) { Diag(Attr.getLoc(), diag::warn_unknown_attribute_ignored) << Attr.getName(); @@ -2004,11 +1999,6 @@ return true; } - if (!checkAttributeNumArgs(*this, Attr, 0)) { - Attr.setInvalid(); - return true; - } - return false; } @@ -5904,6 +5894,9 @@ return true; } + if (S.CheckAttrTarget(Attr)) + return true; + return false; } @@ -6197,6 +6190,9 @@ case AttributeList::AT_NoReturn: handleNoReturnAttr(S, D, Attr); break; + case AttributeList::AT_AnyX86NoCfCheck: + handleNoCfCheckAttr(S, D, Attr); + break; case AttributeList::AT_NoThrow: handleSimpleAttribute(S, D, Attr); break; @@ -6565,7 +6561,7 @@ handleTypeTagForDatatypeAttr(S, D, Attr); break; case AttributeList::AT_AnyX86NoCallerSavedRegisters: - handleNoCallerSavedRegsAttr(S, D, Attr); + handleSimpleAttribute(S, D, Attr); break; case AttributeList::AT_RenderScriptKernel: handleSimpleAttribute(S, D, Attr); Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -124,6 +124,7 @@ case AttributeList::AT_NoReturn: \ case AttributeList::AT_Regparm: \ case AttributeList::AT_AnyX86NoCallerSavedRegisters: \ + case AttributeList::AT_AnyX86NoCfCheck: \ CALLING_CONV_ATTRS_CASELIST // Microsoft-specific type qualifiers. @@ -5127,6 +5128,8 @@ return AttributeList::AT_ObjCOwnership; case AttributedType::attr_noreturn: return AttributeList::AT_NoReturn; + case AttributedType::attr_nocf_check: + return AttributeList::AT_AnyX86NoCfCheck; case AttributedType::attr_cdecl: return AttributeList::AT_CDecl; case AttributedType::attr_fastcall: @@ -6592,7 +6595,7 @@ FunctionTypeUnwrapper unwrapped(S, type); if (attr.getKind() == AttributeList::AT_NoReturn) { - if (S.CheckNoReturnAttr(attr)) + if (S.CheckAttrNoArgs(attr)) return true; // Delay if this is not a function type. @@ -6632,7 +6635,7 @@ } if (attr.getKind() == AttributeList::AT_AnyX86NoCallerSavedRegisters) { - if (S.CheckNoCallerSavedRegsAttr(attr)) + if (S.CheckAttrTarget(attr) || S.CheckAttrNoArgs(attr)) return true; // Delay if this is not a function type. @@ -6645,6 +6648,26 @@ return true; } + if (attr.getKind() == AttributeList::AT_AnyX86NoCfCheck) { + if (!S.getLangOpts().CFProtectionBranch) { + S.Diag(attr.getLoc(), diag::warn_nocf_check_attribute_ignored); + return true; + } + + if (S.CheckAttrTarget(attr) || S.CheckAttrNoArgs(attr)) + return true; + + // If this is not a function type, warning will be asserted by subject + // check. + if (!unwrapped.isFunctionType()) + return true; + + FunctionType::ExtInfo EI = + unwrapped.get()->getExtInfo().withNoCfCheck(true); + type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI)); + return true; + } + if (attr.getKind() == AttributeList::AT_Regparm) { unsigned value; if (S.CheckRegparmAttr(attr, value)) Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -6001,13 +6001,14 @@ } case TYPE_FUNCTION_NO_PROTO: { - if (Record.size() != 7) { + if (Record.size() != 8) { Error("incorrect encoding of no-proto function type"); return QualType(); } QualType ResultType = readType(*Loc.F, Record, Idx); FunctionType::ExtInfo Info(Record[1], Record[2], Record[3], - (CallingConv)Record[4], Record[5], Record[6]); + (CallingConv)Record[4], Record[5], Record[6], + Record[7]); return Context.getFunctionNoProtoType(ResultType, Info); } @@ -6020,9 +6021,10 @@ /*regparm*/ Record[3], static_cast(Record[4]), /*produces*/ Record[5], - /*nocallersavedregs*/ Record[6]); + /*nocallersavedregs*/ Record[6], + /*nocfcheck*/ Record[7]); - unsigned Idx = 7; + unsigned Idx = 8; EPI.Variadic = Record[Idx++]; EPI.HasTrailingReturn = Record[Idx++]; Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -276,6 +276,7 @@ Record.push_back(C.getCC()); Record.push_back(C.getProducesResult()); Record.push_back(C.getNoCallerSavedRegs()); + Record.push_back(C.getNoCfCheck()); if (C.getHasRegParm() || C.getRegParm() || C.getProducesResult()) AbbrevToUse = 0; @@ -884,6 +885,7 @@ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 4)); // CC Abv->Add(BitCodeAbbrevOp(0)); // ProducesResult Abv->Add(BitCodeAbbrevOp(0)); // NoCallerSavedRegs + Abv->Add(BitCodeAbbrevOp(0)); // NoCfCheck // FunctionProtoType Abv->Add(BitCodeAbbrevOp(0)); // IsVariadic Abv->Add(BitCodeAbbrevOp(0)); // HasTrailingReturn Index: test/CodeGen/attributes.c =================================================================== --- test/CodeGen/attributes.c +++ test/CodeGen/attributes.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm -triple i386-linux-gnu -o %t %s +// RUN: %clang_cc1 -emit-llvm -fcf-protection=branch -target-feature +ibt -triple i386-linux-gnu -o %t %s // RUN: FileCheck --input-file=%t %s // CHECK: @t5 = weak global i32 2 @@ -97,8 +97,20 @@ // CHECK: define void @t22() [[NUW]] section ".bar" +// CHECK: define void @t23() [[NOCF_CHECK_FUNC:#[0-9]+]] +void __attribute__((nocf_check)) t23(void) {} + +// CHECK: call void %{{[a-z0-9]+}}() [[NOCF_CHECK_CALL:#[0-9]+]] +typedef void (*f_t)(void); +void t24(f_t f1) { + __attribute__((nocf_check)) f_t p = f1; + (*p)(); +} + // CHECK: attributes [[NUW]] = { noinline nounwind{{.*}} } // CHECK: attributes [[NR]] = { noinline noreturn nounwind{{.*}} } // CHECK: attributes [[COLDDEF]] = { cold {{.*}}} // CHECK: attributes [[COLDDECL]] = { cold {{.*}}} +// CHECK: attributes [[NOCF_CHECK_FUNC]] = { nocf_check {{.*}}} // CHECK: attributes [[COLDSITE]] = { cold {{.*}}} +// CHECK: attributes [[NOCF_CHECK_CALL]] = { nocf_check } Index: test/CodeGen/cetintrin.c =================================================================== --- test/CodeGen/cetintrin.c +++ test/CodeGen/cetintrin.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -ffreestanding %s -triple=i386-apple-darwin -target-feature +shstk -emit-llvm -o - -Wall -Werror | FileCheck %s -// RUN: %clang_cc1 -ffreestanding %s -triple=x86_64-apple-darwin -target-feature +shstk -emit-llvm -o - -Wall -Werror | FileCheck %s --check-prefix=X86_64 +// RUN: %clang_cc1 -ffreestanding %s -triple=i386-unknown-unknown -target-feature +shstk -emit-llvm -o - -Wall -Werror | FileCheck %s +// RUN: %clang_cc1 -ffreestanding %s -triple=x86_64-unknown-unknown -target-feature +shstk -emit-llvm -o - -Wall -Werror | FileCheck %s --check-prefix=X86_64 #include Index: test/CodeGen/x86-cf-protection.c =================================================================== --- test/CodeGen/x86-cf-protection.c +++ test/CodeGen/x86-cf-protection.c @@ -1,5 +1,5 @@ -// RUN: not %clang_cc1 -fsyntax-only -S -emit-llvm -o %t -triple i386-unknown-unknown -fcf-protection=return %s 2>&1 | FileCheck %s --check-prefix=RETURN -// RUN: not %clang_cc1 -fsyntax-only -S -emit-llvm -o %t -triple i386-unknown-unknown -fcf-protection=branch %s 2>&1 | FileCheck %s --check-prefix=BRANCH +// RUN: not %clang_cc1 -fsyntax-only -S -o %t -triple i386-unknown-unknown -fcf-protection=return %s 2>&1 | FileCheck %s --check-prefix=RETURN +// RUN: not %clang_cc1 -fsyntax-only -S -o %t -triple i386-unknown-unknown -fcf-protection=branch %s 2>&1 | FileCheck %s --check-prefix=BRANCH // RETURN: error: option 'cf-protection=return' cannot be specified without '-mshstk' // BRANCH: error: option 'cf-protection=branch' cannot be specified without '-mibt' Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 66 attributes: +// CHECK: #pragma clang attribute supports 67 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -12,6 +12,7 @@ // CHECK-NEXT: AlignValue (SubjectMatchRule_variable, SubjectMatchRule_type_alias) // CHECK-NEXT: AllocSize (SubjectMatchRule_function) // CHECK-NEXT: Annotate () +// CHECK-NEXT: AnyX86NoCfCheck (SubjectMatchRule_hasType_functionType) // CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable)) // CHECK-NEXT: CXX11NoReturn (SubjectMatchRule_function) Index: test/Sema/attr-nocf_check.c =================================================================== --- /dev/null +++ test/Sema/attr-nocf_check.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -verify -fcf-protection=branch -target-feature +ibt -fsyntax-only %s + +// Function pointer definition. +typedef void (*FuncPointerWithNoCfCheck)(void) __attribute__((nocf_check)); // no-warning +typedef void (*FuncPointer)(void); + +// Dont allow function declaration and definition mismatch. +void __attribute__((nocf_check)) testNoCfCheck(); // expected-note {{previous declaration is here}} +void testNoCfCheck(){}; // expected-error {{conflicting types for 'testNoCfCheck'}} + +// No variable or parameter declaration +__attribute__((nocf_check)) int i; // expected-warning {{'nocf_check' attribute only applies to function}} +void testNoCfCheckImpl(double __attribute__((nocf_check)) i) {} // expected-warning {{'nocf_check' attribute only applies to function}} + +// Allow attributed function pointers as well as casting between attributed +// and non-attributed function pointers. +void testNoCfCheckMismatch(FuncPointer f) { + FuncPointerWithNoCfCheck fNoCfCheck = f; // expected-warning {{incompatible function pointer types}} + (*fNoCfCheck)(); // no-warning +} + +// 'nocf_check' Attribute has no parameters. +int testNoCfCheckParams() __attribute__((nocf_check(1))); // expected-error {{'nocf_check' attribute takes no arguments}} Index: test/Sema/attr-nocf_check.cpp =================================================================== --- /dev/null +++ test/Sema/attr-nocf_check.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -triple=i386-unknown-unknown -verify -fcf-protection=branch -target-feature +ibt -std=c++11 -fsyntax-only %s + +// Function pointer definition. +typedef void (*FuncPointerWithNoCfCheck)(void) __attribute__((nocf_check)); // no-warning +typedef void (*FuncPointer)(void); + +// Dont allow function declaration and definition mismatch. +void __attribute__((nocf_check)) testNoCfCheck(); // expected-note {{previous declaration is here}} +void testNoCfCheck(){}; // expected-error {{conflicting types for 'testNoCfCheck'}} + +// No variable or parameter declaration +__attribute__((nocf_check)) int i; // expected-warning {{'nocf_check' attribute only applies to function}} +void testNoCfCheckImpl(double __attribute__((nocf_check)) i) {} // expected-warning {{'nocf_check' attribute only applies to function}} + +// Allow attributed function pointers as well as casting between attributed +// and non-attributed function pointers. +void testNoCfCheckMismatch(FuncPointer f) { + FuncPointerWithNoCfCheck fNoCfCheck = f; // expected-error {{cannot initialize a variable of type 'FuncPointerWithNoCfCheck'}} + (*fNoCfCheck)(); // no-warning +} + +// 'nocf_check' Attribute has no parameters. +int testNoCfCheckParams() __attribute__((nocf_check(1))); // expected-error {{'nocf_check' attribute takes no arguments}} Index: test/Sema/nocf_check_attr_not_allowed.c =================================================================== --- /dev/null +++ test/Sema/nocf_check_attr_not_allowed.c @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -fsyntax-only -verify -fcf-protection=branch %s +// RUN: %clang_cc1 -triple arm-unknown-linux-gnu -fsyntax-only -verify -fcf-protection=branch %s + +void __attribute__((nocf_check)) foo(); // expected-warning {{unknown attribute 'nocf_check' ignored}}