https://github.com/cooperp updated https://github.com/llvm/llvm-project/pull/173311
>From d2df5e0996fdb5011ca6f63807df2472dd8718ba Mon Sep 17 00:00:00 2001 From: Peter Cooper <[email protected]> Date: Tue, 13 Jan 2026 21:52:05 -0800 Subject: [PATCH] Allow the no_stack_protector attribute on local variables. This attribute is currently only permitted on functions, but sometimes it can be useful to opt out a specific local variable. That way the function can still get a stack protector if other locals require it, but locals which are opted out won't have the overhead of a stack protector if not needed. --- clang/docs/ReleaseNotes.rst | 5 ++ clang/include/clang/Basic/Attr.td | 7 ++ clang/include/clang/Basic/AttrDocs.td | 14 ++++ .../clang/Basic/DiagnosticCommonKinds.td | 4 ++ clang/lib/CodeGen/CGDecl.cpp | 15 ++++ clang/lib/CodeGen/CodeGenModule.cpp | 31 +++++--- clang/lib/CodeGen/CodeGenModule.h | 3 + clang/test/CodeGen/stack-protector-vars.cpp | 71 +++++++++++++++++++ ...a-attribute-supported-attributes-list.test | 1 + clang/test/Sema/stack_protector_ignore.c | 11 +++ 10 files changed, 151 insertions(+), 11 deletions(-) create mode 100644 clang/test/CodeGen/stack-protector-vars.cpp create mode 100644 clang/test/Sema/stack_protector_ignore.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b75715e873c50..37df3aab16c52 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -114,6 +114,11 @@ Removed Compiler Flags Attribute Changes in Clang -------------------------- +- Added new attribute ``stack_protector_ignore`` to opt specific local variables out of + the analysis which determines if a function should get a stack protector. A function + will still generate a stack protector if other local variables or command line flags + require it. + Improvements to Clang's diagnostics ----------------------------------- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index ba44266d22c8c..2335168e4510c 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2725,6 +2725,13 @@ def NoStackProtector : InheritableAttr { let SimpleHandler = 1; } +def StackProtectorIgnore : InheritableAttr { + let Spellings = [Clang<"stack_protector_ignore">]; + let Subjects = SubjectList<[LocalVar]>; + let Documentation = [StackProtectorIgnoreDocs]; + let SimpleHandler = 1; +} + def StrictGuardStackCheck : InheritableAttr { let Spellings = [Declspec<"strict_gs_check">]; let Subjects = SubjectList<[Function]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 812b48058d189..6e2c73f924352 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -5736,6 +5736,20 @@ option. }]; } +def StackProtectorIgnoreDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``stack_protector_ignore`` attribute skips analysis of the given local +variable when determining if a function should use a stack protector. + +The ``-fstack-protector`` option uses a heuristic to only add stack protectors +to functions which contain variables or buffers over some size threshold. This +attribute overrides that heuristic for the attached variable, opting +them out. If this results in no variables or buffers remaining over the stack +protector threshold, then the function will no longer use a stack protector. + }]; +} + def StrictGuardStackCheckDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 6e50e225a8cc1..a69b91bc93a28 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -312,6 +312,10 @@ def warn_stack_clash_protection_inline_asm : Warning< "unable to protect inline asm that clobbers stack pointer against stack " "clash">, InGroup<DiagGroup<"stack-protector">>; +def warn_stack_protection_ignore_attribute : Warning< + "'stack_protector_ignore' attribute ignored due to " + "'-fstack-protector-all' option">; + def warn_slh_does_not_support_asm_goto : Warning< "speculative load hardening does not protect functions with asm goto">, InGroup<DiagGroup<"slh-asm-goto">>; diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 8b1cd83af2396..f39e282f3d055 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1635,6 +1635,21 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { assert(!emission.useLifetimeMarkers()); } } + + if (D.hasAttr<StackProtectorIgnoreAttr>()) { + if (auto *AI = dyn_cast<llvm::AllocaInst>(address.getBasePointer())) { + llvm::LLVMContext &Ctx = Builder.getContext(); + auto *Operand = llvm::ConstantAsMetadata::get(Builder.getInt32(0)); + AI->setMetadata("stack-protector", llvm::MDNode::get(Ctx, {Operand})); + } + + std::optional<llvm::Attribute::AttrKind> Attr = + CGM.StackProtectorAttribute(&D); + if (Attr && (*Attr == llvm::Attribute::StackProtectReq)) { + CGM.getDiags().Report(D.getLocation(), + diag::warn_stack_protection_ignore_attribute); + } + } } else { EnsureInsertPoint(); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index dc8a31b7f7f0d..728546b114805 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -950,6 +950,22 @@ static bool isStackProtectorOn(const LangOptions &LangOpts, return LangOpts.getStackProtector() == Mode; } +std::optional<llvm::Attribute::AttrKind> +CodeGenModule::StackProtectorAttribute(const Decl *D) const { + if (D && D->hasAttr<NoStackProtectorAttr>()) + ; // Do nothing. + else if (D && D->hasAttr<StrictGuardStackCheckAttr>() && + isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPOn)) + return llvm::Attribute::StackProtectStrong; + else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPOn)) + return llvm::Attribute::StackProtect; + else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPStrong)) + return llvm::Attribute::StackProtectStrong; + else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq)) + return llvm::Attribute::StackProtectReq; + return std::nullopt; +} + void CodeGenModule::Release() { Module *Primary = getContext().getCurrentNamedModule(); if (CXX20ModuleInits && Primary && !Primary->isHeaderLikeModule()) @@ -2736,17 +2752,10 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, if (!hasUnwindExceptions(LangOpts)) B.addAttribute(llvm::Attribute::NoUnwind); - if (D && D->hasAttr<NoStackProtectorAttr>()) - ; // Do nothing. - else if (D && D->hasAttr<StrictGuardStackCheckAttr>() && - isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPOn)) - B.addAttribute(llvm::Attribute::StackProtectStrong); - else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPOn)) - B.addAttribute(llvm::Attribute::StackProtect); - else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPStrong)) - B.addAttribute(llvm::Attribute::StackProtectStrong); - else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq)) - B.addAttribute(llvm::Attribute::StackProtectReq); + if (std::optional<llvm::Attribute::AttrKind> Attr = + StackProtectorAttribute(D)) { + B.addAttribute(*Attr); + } if (!D) { // Non-entry HLSL functions must always be inlined. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index e0e3284f72168..cc18e21d45759 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1872,6 +1872,9 @@ class CodeGenModule : public CodeGenTypeCache { return TrapReasonBuilder(&getDiags(), DiagID, TR); } + std::optional<llvm::Attribute::AttrKind> + StackProtectorAttribute(const Decl *D) const; + private: bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const; diff --git a/clang/test/CodeGen/stack-protector-vars.cpp b/clang/test/CodeGen/stack-protector-vars.cpp new file mode 100644 index 0000000000000..d11cd822cd871 --- /dev/null +++ b/clang/test/CodeGen/stack-protector-vars.cpp @@ -0,0 +1,71 @@ +// RUN: %clang -target x86_64-apple-darwin -emit-llvm -S -o - %s | FileCheck %s +// RUN: %clang -target x86_64-apple-darwin -emit-llvm -S -o - %s -fstack-protector | FileCheck %s +// RUN: %clang -target x86_64-apple-darwin -emit-llvm -S -o - %s -fstack-protector-all | FileCheck %s +// RUN: %clang -target x86_64-apple-darwin -Xclang -verify -fstack-protector-all %s -o %t -c + +typedef __SIZE_TYPE__ size_t; + +int printf(const char * _Format, ...); +char *strcpy(char *s1, const char *s2); + +struct S { + S(); + int a[4]; +}; + +// CHECK: define {{.*}} @_Z5test1PKc +// CHECK: %{{.*}} = alloca [1000 x i8], align {{.*}}, !stack-protector ![[A:.*]] +void test1(const char *msg) { + __attribute__((stack_protector_ignore)) + char a[1000]; // expected-warning {{'stack_protector_ignore' attribute ignored due to '-fstack-protector-all' option}} + strcpy(a, msg); + printf("%s\n", a); +} + +// CHECK: define {{.*}} @_Z5test2 +// CHECK-NOT: %{{.*}} = alloca [1000 x i8], align {{.*}}, !stack-protector +void test2(const char *msg) { + char b[1000]; + strcpy(b, msg); + printf("%s\n", b); +} + +// CHECK: define {{.*}} @_Z5test3v +// CHECK: %{{.*}} = alloca %struct.S, align {{.*}}, !stack-protector ![[A:.*]] +S test3() { + __attribute__((stack_protector_ignore)) + S s; // expected-warning {{'stack_protector_ignore' attribute ignored due to '-fstack-protector-all' option}} + return s; +} + +// CHECK: define {{.*}} @_Z5test4b +// CHECK: %{{.*}} = alloca %struct.S, align {{.*}}, !stack-protector ![[A:.*]] +// CHECK: call void @_ZN1SC1Ev +S test4(bool b) { + __attribute__((stack_protector_ignore)) + S s; // expected-warning {{'stack_protector_ignore' attribute ignored due to '-fstack-protector-all' option}} + if ( b ) + return s; + else + return s; +} + +// CHECK: define {{.*}} @_Z5test5b +// CHECK: %{{.*}} = alloca %struct.S, align {{.*}} +// CHECK-NOT: stack-protector +// CHECK: %{{.*}} = alloca %struct.S, align {{.*}}, !stack-protector ![[A:.*]] +// CHECK: %{{.*}} = alloca %struct.S, align {{.*}} +// CHECK-NOT: stack-protector +// CHECK: call void @_ZN1SC1Ev +// CHECK: call void @_ZN1SC1Ev +S test5(bool b) { + __attribute__((stack_protector_ignore)) + S s1; // expected-warning {{'stack_protector_ignore' attribute ignored due to '-fstack-protector-all' option}} + S s2; + if ( b ) + return s1; + else + return s2; +} + +// CHECK: ![[A]] = !{i32 0} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 3114d2fc9e693..d6d900f3caf84 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -198,6 +198,7 @@ // CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property) // CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: SpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method) +// CHECK-NEXT: StackProtectorIgnore (SubjectMatchRule_variable_is_local) // CHECK-NEXT: StandaloneDebug (SubjectMatchRule_record) // CHECK-NEXT: SwiftAsync (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: SwiftAsyncContext (SubjectMatchRule_variable_is_parameter) diff --git a/clang/test/Sema/stack_protector_ignore.c b/clang/test/Sema/stack_protector_ignore.c new file mode 100644 index 0000000000000..b6f76335957d7 --- /dev/null +++ b/clang/test/Sema/stack_protector_ignore.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int __attribute__((stack_protector_ignore)) global_var; // expected-warning {{'stack_protector_ignore' attribute only applies to local variables}} + +void __attribute__((stack_protector_ignore)) func(void) {} // expected-warning {{'stack_protector_ignore' attribute only applies to local variables}} + +void func2(void) { + __attribute__((stack_protector_ignore)) int var; + __attribute__((stack_protector_ignore)) static int var2; // expected-warning {{'stack_protector_ignore' attribute only applies to local variables}} + __attribute__((stack_protector_ignore(2))) int var3; // expected-error {{'stack_protector_ignore' attribute takes no arguments}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
