https://github.com/cooperp updated 
https://github.com/llvm/llvm-project/pull/173311

>From 50d1dc1f26ab99afd5887a471efaffe5beab94be 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           | 32 ++++++---
 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, 152 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 b91d99647855c..dd2980152276d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -103,6 +103,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 efa64dbe4b51e..2e61deee5b6a5 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2723,6 +2723,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..e4642fda88b9f 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 85ed38f144627..c04a7e8e22d5c 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -950,6 +950,23 @@ 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())
@@ -2726,17 +2743,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 38b052e5cd1dd..9feb42d375154 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1869,6 +1869,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

Reply via email to