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

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.

This is paired with an LLVM change which has already landed, which uses the new 
metadata to opt specific alloca instructions out of requiring a stack protector.

>From 437965b256cb7a23845a0fc3d299838edb88b104 Mon Sep 17 00:00:00 2001
From: Peter Cooper <[email protected]>
Date: Sat, 8 Nov 2025 10:14:14 -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/include/clang/Basic/Attr.td             |  2 +-
 clang/lib/CodeGen/CGDecl.cpp                  |  8 ++++++
 clang/test/CodeGen/stack-protector-vars.c     | 25 +++++++++++++++++++
 ...a-attribute-supported-attributes-list.test |  2 +-
 clang/test/Sema/no_stack_protector.c          |  7 +++++-
 5 files changed, 41 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGen/stack-protector-vars.c

diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index f85d2da21eab9..10cb3c64f54d2 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2726,7 +2726,7 @@ def : MutualExclusions<[AlwaysInline, NotTailCalled]>;
 def NoStackProtector : InheritableAttr {
   let Spellings = [Clang<"no_stack_protector">, CXX11<"gnu", 
"no_stack_protector">,
                    C23<"gnu", "no_stack_protector">, Declspec<"safebuffers">];
-  let Subjects = SubjectList<[Function]>;
+  let Subjects = SubjectList<[Function, LocalVar]>;
   let Documentation = [NoStackProtectorDocs];
   let SimpleHandler = 1;
 }
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8b1cd83af2396..ae8ca92d8860f 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1604,6 +1604,14 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
                                  allocaAlignment, D.getName(),
                                  /*ArraySize=*/nullptr, &AllocaAddr);
 
+      if (D.hasAttr<NoStackProtectorAttr>()) {
+        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}));
+        }
+      }
+
       // Don't emit lifetime markers for MSVC catch parameters. The lifetime of
       // the catch parameter starts in the catchpad instruction, and we can't
       // insert code in those basic blocks.
diff --git a/clang/test/CodeGen/stack-protector-vars.c 
b/clang/test/CodeGen/stack-protector-vars.c
new file mode 100644
index 0000000000000..218cdc33b3010
--- /dev/null
+++ b/clang/test/CodeGen/stack-protector-vars.c
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+
+typedef __SIZE_TYPE__ size_t;
+
+int printf(const char * _Format, ...);
+char *strcpy(char *s1, const char *s2);
+
+// CHECK: define {{.*}}void @test1
+// CHECK: %a = alloca [1000 x i8], align 1, !stack-protector ![[A:.*]]
+void test1(const char *msg) {
+  __attribute__((no_stack_protector))
+  char a[1000];
+  strcpy(a, msg);
+  printf("%s\n", a);
+}
+
+// CHECK: define {{.*}}void @test2
+// CHECK-NOT: %b = alloca [1000 x i8], align 1, !stack-protector
+void test2(const char *msg) {
+  char b[1000];
+  strcpy(b, msg);
+  printf("%s\n", b);
+}
+
+// CHECK: ![[A]] = !{i32 0}
\ No newline at end of file
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test 
b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 3114d2fc9e693..6420a298f4dc9 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -138,7 +138,7 @@
 // CHECK-NEXT: NoSanitizeThread (SubjectMatchRule_function)
 // CHECK-NEXT: NoSpeculativeLoadHardening (SubjectMatchRule_function, 
SubjectMatchRule_objc_method)
 // CHECK-NEXT: NoSplitStack (SubjectMatchRule_function)
-// CHECK-NEXT: NoStackProtector (SubjectMatchRule_function)
+// CHECK-NEXT: NoStackProtector (SubjectMatchRule_function, 
SubjectMatchRule_variable_is_local)
 // CHECK-NEXT: NoThreadSafetyAnalysis (SubjectMatchRule_function)
 // CHECK-NEXT: NoThrow (SubjectMatchRule_hasType_functionType)
 // CHECK-NEXT: NoUwtable (SubjectMatchRule_hasType_functionType)
diff --git a/clang/test/Sema/no_stack_protector.c 
b/clang/test/Sema/no_stack_protector.c
index 1ecd46bc624ce..fab3df667883a 100644
--- a/clang/test/Sema/no_stack_protector.c
+++ b/clang/test/Sema/no_stack_protector.c
@@ -4,5 +4,10 @@
 [[clang::no_stack_protector]] void test2(void) {}
 
 void __attribute__((no_stack_protector)) foo(void) {}
-int __attribute__((no_stack_protector)) var; // expected-warning 
{{'no_stack_protector' attribute only applies to functions}}
+int __attribute__((no_stack_protector)) var; // expected-warning 
{{'no_stack_protector' attribute only applies to functions and local variables}}
 void  __attribute__((no_stack_protector(2))) bar(void) {} // expected-error 
{{'no_stack_protector' attribute takes no arguments}}
+
+void func()
+{
+       int __attribute__((no_stack_protector)) localvar;
+}
\ No newline at end of file

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to