Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -3149,6 +3149,12 @@ let Documentation = [SpeculativeLoadHardeningDocs]; } +def NoSpeculativeLoadHardening : InheritableAttr { + let Spellings = [Clang<"no_speculative_load_hardening">]; + let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>; + let Documentation = [NoSpeculativeLoadHardeningDocs]; +} + def Uninitialized : InheritableAttr { let Spellings = [Clang<"uninitialized", 0>]; let Subjects = SubjectList<[LocalVar]>; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -3822,7 +3822,8 @@ This attribute can be applied to a function declaration in order to indicate that `Speculative Load Hardening `_ should be enabled for the function body. This can also be applied to a method - in Objective C. + in Objective C. This attribute will take precedence over the command line flag in + the case where `-mno-speculative-load-hardening `_ is specified. Speculative Load Hardening is a best-effort mitigation against information leak attacks that make use of control flow @@ -3840,6 +3841,42 @@ }]; } +def NoSpeculativeLoadHardeningDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ + This attribute can be applied to a function declaration in order to indicate + that `Speculative Load Hardening `_ + is *not* needed for the function body. This can also be applied to a method + in Objective C. This attribute will take precedence over the command line flag in + the case where `-mspeculative-load-hardening `_ is specified. + + Warning: This attribute may not prevent Speculative Load Hardening from being + enabled for a function which inlines a function that has the + 'speculative_load_hardening' attribute. This is intended to provide a + maximally conservative model where the code that is marked with the + 'speculative_load_hardening' attribute will always (even when inlined) + be hardened. A user of this attribute may want to mark functions called by + a function they do not want to be hardened with the 'noinline' attribute. + + For example: + + .. code-block:: c + + __attribute__((speculative_load_hardening)) + int foo(int i) { + return i; + } + + // Note: bar() may still have speculative load hardening enabled if + // foo() is inlined into bar(). Mark foo() with __attribute__((noinline)) + // to avoid this situation. + __attribute__((no_speculative_load_hardening)) + int bar(int i) { + return foo(i); + } + }]; +} + def ObjCExternallyRetainedDocs : Documentation { let Category = DocCatVariable; let Content = [{ Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2496,6 +2496,12 @@ unsigned AttrSpellingListIndex); MinSizeAttr *mergeMinSizeAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex); + NoSpeculativeLoadHardeningAttr * + mergeNoSpeculativeLoadHardeningAttr(Decl *D, + const NoSpeculativeLoadHardeningAttr &AL); + SpeculativeLoadHardeningAttr * + mergeSpeculativeLoadHardeningAttr(Decl *D, + const SpeculativeLoadHardeningAttr &AL); OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex); InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL); Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -1793,8 +1793,6 @@ if (CodeGenOpts.Backchain) FuncAttrs.addAttribute("backchain"); - // FIXME: The interaction of this attribute with the SLH command line flag - // has not been determined. if (CodeGenOpts.SpeculativeLoadHardening) FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening); } @@ -1864,8 +1862,6 @@ FuncAttrs.addAttribute(llvm::Attribute::NoDuplicate); if (TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::Convergent); - if (TargetDecl->hasAttr()) - FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening); if (const FunctionDecl *Fn = dyn_cast(TargetDecl)) { AddAttributesFromFunctionProtoType( @@ -1910,6 +1906,16 @@ ConstructDefaultFnAttrList(Name, HasOptnone, AttrOnCallSite, FuncAttrs); + // This must run after constructing the default function attribute list + // to ensure that the speculative load hardening attribute is removed + // in the case where the -mspeculative-load-hardening flag was passed. + if (TargetDecl) { + if (TargetDecl->hasAttr()) + FuncAttrs.removeAttribute(llvm::Attribute::SpeculativeLoadHardening); + if (TargetDecl->hasAttr()) + FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening); + } + if (CodeGenOpts.EnableSegmentedStacks && !(TargetDecl && TargetDecl->hasAttr())) FuncAttrs.addAttribute("split-stack"); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -2489,6 +2489,10 @@ else if (const auto *UA = dyn_cast(Attr)) NewAttr = S.mergeUuidAttr(D, UA->getRange(), AttrSpellingListIndex, UA->getGuid()); + else if (const auto *SLHA = dyn_cast(Attr)) + NewAttr = S.mergeSpeculativeLoadHardeningAttr(D, *SLHA); + else if (const auto *SLHA = dyn_cast(Attr)) + NewAttr = S.mergeNoSpeculativeLoadHardeningAttr(D, *SLHA); else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr)) NewAttr = cast(Attr->clone(S.Context)); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -4157,6 +4157,15 @@ return ::new (Context) MinSizeAttr(Range, Context, AttrSpellingListIndex); } +NoSpeculativeLoadHardeningAttr *Sema::mergeNoSpeculativeLoadHardeningAttr( + Decl *D, const NoSpeculativeLoadHardeningAttr &AL) { + if (checkAttrMutualExclusion(*this, D, AL)) + return nullptr; + + return ::new (Context) NoSpeculativeLoadHardeningAttr( + AL.getRange(), Context, AL.getSpellingListIndex()); +} + OptimizeNoneAttr *Sema::mergeOptimizeNoneAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex) { if (AlwaysInlineAttr *Inline = D->getAttr()) { @@ -4177,6 +4186,15 @@ AttrSpellingListIndex); } +SpeculativeLoadHardeningAttr *Sema::mergeSpeculativeLoadHardeningAttr( + Decl *D, const SpeculativeLoadHardeningAttr &AL) { + if (checkAttrMutualExclusion(*this, D, AL)) + return nullptr; + + return ::new (Context) SpeculativeLoadHardeningAttr( + AL.getRange(), Context, AL.getSpellingListIndex()); +} + static void handleAlwaysInlineAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (checkAttrMutualExclusion(S, D, AL)) return; @@ -6618,7 +6636,13 @@ handleSectionAttr(S, D, AL); break; case ParsedAttr::AT_SpeculativeLoadHardening: - handleSimpleAttribute(S, D, AL); + handleSimpleAttributeWithExclusions(S, D, + AL); + break; + case ParsedAttr::AT_NoSpeculativeLoadHardening: + handleSimpleAttributeWithExclusions(S, D, AL); break; case ParsedAttr::AT_CodeSeg: handleCodeSegAttr(S, D, AL); Index: test/CodeGen/attr-speculative-load-hardening.cpp =================================================================== --- test/CodeGen/attr-speculative-load-hardening.cpp +++ test/CodeGen/attr-speculative-load-hardening.cpp @@ -1,18 +0,0 @@ -// RUN: %clang_cc1 -std=c++11 -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK1 -// RUN: %clang_cc1 -std=c++11 -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK2 -// -// Check that we set the attribute on each function. - -[[clang::speculative_load_hardening]] -int test1() { - return 42; -} - -int __attribute__((speculative_load_hardening)) test2() { - return 42; -} -// CHECK1: @{{.*}}test1{{.*}}[[SLH1:#[0-9]+]] -// CHECK1: attributes [[SLH1]] = { {{.*}}speculative_load_hardening{{.*}} } - -// CHECK2: @{{.*}}test2{{.*}}[[SLH2:#[0-9]+]] -// CHECK2: attributes [[SLH2]] = { {{.*}}speculative_load_hardening{{.*}} } Index: test/CodeGen/attr-speculative-load-hardening.m =================================================================== --- test/CodeGen/attr-speculative-load-hardening.m +++ test/CodeGen/attr-speculative-load-hardening.m @@ -1,9 +0,0 @@ -// RUN: %clang -emit-llvm %s -o - -S | FileCheck %s -check-prefix=SLH - -int main() __attribute__((speculative_load_hardening)) { - return 0; -} - -// SLH: @{{.*}}main{{.*}}[[SLH:#[0-9]+]] - -// SLH: attributes [[SLH]] = { {{.*}}speculative_load_hardening{{.*}} } Index: test/CodeGenCXX/attr-speculative-load-hardening.cpp =================================================================== --- test/CodeGenCXX/attr-speculative-load-hardening.cpp +++ test/CodeGenCXX/attr-speculative-load-hardening.cpp @@ -0,0 +1,62 @@ +// Check that we correctly set or did not set the attribute for each function. +// RUN: %clang_cc1 -std=c++11 -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK1 +// RUN: %clang_cc1 -std=c++11 -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK2 + +// Check that we correctly set or did not set the attribute on each function despite the +// -mspeculative-load-hardening flag. +// RUN: %clang_cc1 -mspeculative-load-hardening -std=c++11 -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK3 +// RUN: %clang_cc1 -mspeculative-load-hardening -std=c++11 -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK4 + + +// Check that we correctly set or did not set the attribute on each function despite the +// -mno-speculative-load-hardening flag. +// RUN: %clang -mno-speculative-load-hardening -S -std=c++11 -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK5 +// RUN: %clang -mno-speculative-load-hardening -S -std=c++11 -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK6 + + +[[clang::speculative_load_hardening]] +int test1() { + return 42; +} + +int __attribute__((speculative_load_hardening)) test2() { + return 42; +} + +[[clang::no_speculative_load_hardening]] +int test3() { + return 42; +} + +int __attribute__((no_speculative_load_hardening)) test4() { + return 42; +} +// CHECK1: @{{.*}}test1{{.*}}[[SLH:#[0-9]+]] +// CHECK1: @{{.*}}test3{{.*}}[[NOSLH:#[0-9]+]] +// CHECK1: attributes [[SLH]] = { {{.*}}speculative_load_hardening{{.*}} } +// CHECK1-NOT: attributes [[NOSLH]] = { {{.*}}speculative_load_hardening{{.*}} } + +// CHECK2: @{{.*}}test2{{.*}}[[SLH:#[0-9]+]] +// CHECK2: @{{.*}}test4{{.*}}[[NOSLH:#[0-9]+]] +// CHECK2: attributes [[SLH]] = { {{.*}}speculative_load_hardening{{.*}} } +// CHECK2-NOT: attributes [[NOSLH]] = { {{.*}}speculative_load_hardening{{.*}} } + +// CHECK3: @{{.*}}test1{{.*}}[[SLH:#[0-9]+]] +// CHECK3: @{{.*}}test3{{.*}}[[NOSLH:#[0-9]+]] +// CHECK3: attributes [[SLH]] = { {{.*}}speculative_load_hardening{{.*}} } +// CHECK3-NOT: attributes [[NOSLH]] = { {{.*}}speculative_load_hardening{{.*}} } + +// CHECK4: @{{.*}}test2{{.*}}[[SLH:#[0-9]+]] +// CHECK4: @{{.*}}test4{{.*}}[[NOSLH:#[0-9]+]] +// CHECK4: attributes [[SLH]] = { {{.*}}speculative_load_hardening{{.*}} } +// CHECK4-NOT: attributes [[NOSLH]] = { {{.*}}speculative_load_hardening{{.*}} } + +// CHECK5: @{{.*}}test1{{.*}}[[SLH:#[0-9]+]] +// CHECK5: @{{.*}}test3{{.*}}[[NOSLH:#[0-9]+]] +// CHECK5: attributes [[SLH]] = { {{.*}}speculative_load_hardening{{.*}} } +// CHECK5-NOT: attributes [[NOSLH]] = { {{.*}}speculative_load_hardening{{.*}} } + +// CHECK6: @{{.*}}test2{{.*}}[[SLH:#[0-9]+]] +// CHECK6: @{{.*}}test4{{.*}}[[NOSLH:#[0-9]+]] +// CHECK6: attributes [[SLH]] = { {{.*}}speculative_load_hardening{{.*}} } +// CHECK6-NOT: attributes [[NOSLH]] = { {{.*}}speculative_load_hardening{{.*}} } Index: test/CodeGenObjC/attr-speculative-load-hardening.m =================================================================== --- test/CodeGenObjC/attr-speculative-load-hardening.m +++ test/CodeGenObjC/attr-speculative-load-hardening.m @@ -0,0 +1,14 @@ +// RUN: %clang -emit-llvm %s -o - -S | FileCheck %s -check-prefix=SLH + +int main() __attribute__((speculative_load_hardening)) { + return 0; +} + +int test() __attribute__((no_speculative_load_hardening)) { + return 0; +} + +// SLH: @{{.*}}main{{.*}}[[SLH:#[0-9]+]] +// SLH: @{{.*}}test{{.*}}[[NOSLH:#[0-9]+]] +// SLH: attributes [[SLH]] = { {{.*}}speculative_load_hardening{{.*}} } +// SLH-NOT: attributes [[NOSLH]] = { {{.*}}speculative_load_hardening{{.*}} } 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 @@ -80,6 +80,7 @@ // CHECK-NEXT: NoMips16 (SubjectMatchRule_function) // CHECK-NEXT: NoSanitize (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_variable_is_global) // CHECK-NEXT: NoSanitizeSpecific (SubjectMatchRule_function, SubjectMatchRule_variable_is_global) +// CHECK-NEXT: NoSpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: NoSplitStack (SubjectMatchRule_function) // CHECK-NEXT: NoStackProtector (SubjectMatchRule_function) // CHECK-NEXT: NoThreadSafetyAnalysis (SubjectMatchRule_function) Index: test/SemaCXX/attr-no-speculative-load-hardening.cpp =================================================================== --- test/SemaCXX/attr-no-speculative-load-hardening.cpp +++ test/SemaCXX/attr-no-speculative-load-hardening.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +int i __attribute__((no_speculative_load_hardening)); // expected-error {{'no_speculative_load_hardening' attribute only applies to functions}} + +void f1() __attribute__((no_speculative_load_hardening)); +void f2() __attribute__((no_speculative_load_hardening(1))); // expected-error {{'no_speculative_load_hardening' attribute takes no arguments}} + +template +void tf1() __attribute__((no_speculative_load_hardening)); + +int f3(int __attribute__((no_speculative_load_hardening)), int); // expected-error {{'no_speculative_load_hardening' attribute only applies to functions}} + +struct A { + int f __attribute__((no_speculative_load_hardening)); // expected-error {{'no_speculative_load_hardening' attribute only applies to functions}} + void mf1() __attribute__((no_speculative_load_hardening)); + static void mf2() __attribute__((no_speculative_load_hardening)); +}; + +int ci [[clang::no_speculative_load_hardening]]; // expected-error {{'no_speculative_load_hardening' attribute only applies to functions}} + +[[clang::no_speculative_load_hardening]] void cf1(); +[[clang::no_speculative_load_hardening(1)]] void cf2(); // expected-error {{'no_speculative_load_hardening' attribute takes no arguments}} + +template +[[clang::no_speculative_load_hardening]] +void ctf1(); + +int cf3(int c[[clang::no_speculative_load_hardening]], int); // expected-error {{'no_speculative_load_hardening' attribute only applies to functions}} + +struct CA { + int f [[clang::no_speculative_load_hardening]]; // expected-error {{'no_speculative_load_hardening' attribute only applies to functions}} + [[clang::no_speculative_load_hardening]] void mf1(); + [[clang::no_speculative_load_hardening]] static void mf2(); +}; Index: test/SemaCXX/attr-speculative-load-hardening.cpp =================================================================== --- test/SemaCXX/attr-speculative-load-hardening.cpp +++ test/SemaCXX/attr-speculative-load-hardening.cpp @@ -16,6 +16,17 @@ static void mf2() __attribute__((speculative_load_hardening)); }; +void f4() __attribute__((no_speculative_load_hardening, speculative_load_hardening)); // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} + +void f5() __attribute__((speculative_load_hardening, no_speculative_load_hardening)); // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} + +void f6() __attribute__((no_speculative_load_hardening)); + +void f6() __attribute__((speculative_load_hardening)); // expected-error@-2 {{'no_speculative_load_hardening' and 'speculative_load_hardening' attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} + int ci [[clang::speculative_load_hardening]]; // expected-error {{'speculative_load_hardening' attribute only applies to functions}} [[clang::speculative_load_hardening]] void cf1(); @@ -32,3 +43,16 @@ [[clang::speculative_load_hardening]] void mf1(); [[clang::speculative_load_hardening]] static void mf2(); }; + +[[clang::speculative_load_hardening, clang::no_speculative_load_hardening]] void cf4(); // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} + +[[clang::no_speculative_load_hardening, clang::speculative_load_hardening]] void cf5(); // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} + +[[clang::speculative_load_hardening]] +void cf6(); + +[[clang::no_speculative_load_hardening]] +void cf6(); // expected-error@-4 {{'speculative_load_hardening' and 'no_speculative_load_hardening' attributes are not compatible}} \ +// expected-note@-1 {{conflicting attribute is here}}