Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -3131,3 +3131,9 @@ let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>; let Documentation = [SpeculativeLoadHardeningDocs]; } + +def NoSpeculativeLoadHardening : InheritableAttr { + let Spellings = [Clang<"no_speculative_load_hardening">]; + let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>; + let Documentation = [NoSpeculativeLoadHardeningDocs]; +} Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -3745,7 +3745,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 @@ -3762,3 +3763,39 @@ (even after inlining) end up hardened. }]; } + +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); + } + }]; +} Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2489,6 +2489,8 @@ 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: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/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); } @@ -1858,8 +1856,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( @@ -1904,6 +1900,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: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/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: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -4132,6 +4132,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()) { @@ -4152,6 +4161,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; @@ -6446,7 +6464,12 @@ 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: clang/test/CodeGen/attr-speculative-load-hardening.cpp =================================================================== --- clang/test/CodeGen/attr-speculative-load-hardening.cpp +++ /dev/null @@ -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: clang/test/CodeGenCXX/attr-speculative-load-hardening.cpp =================================================================== --- /dev/null +++ clang/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: clang/test/CodeGenObjC/attr-speculative-load-hardening.m =================================================================== --- clang/test/CodeGenObjC/attr-speculative-load-hardening.m +++ clang/test/CodeGenObjC/attr-speculative-load-hardening.m @@ -4,6 +4,11 @@ return 0; } -// SLH: @{{.*}}main{{.*}}[[SLH:#[0-9]+]] +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: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ clang/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: clang/test/SemaCXX/attr-no-speculative-load-hardening.cpp =================================================================== --- /dev/null +++ clang/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: clang/test/SemaCXX/attr-speculative-load-hardening.cpp =================================================================== --- clang/test/SemaCXX/attr-speculative-load-hardening.cpp +++ clang/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}}