[clang] Allow the no_stack_protector attribute on local variables (PR #173311)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 6 21:33:06 PST 2026
https://github.com/cooperp updated https://github.com/llvm/llvm-project/pull/173311
>From 1d8b79c0eb131ec470827b51e8f03e47cac53014 Mon Sep 17 00:00:00 2001
From: Peter Cooper <peter_cooper at apple.com>
Date: Sat, 8 Nov 2025 10:14:14 -0800
Subject: [PATCH 1/3] 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 b017906a8d690..2a31626ca2471 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
>From 69c6d23ecac2e95933d7b03234151c1d3001cb1e Mon Sep 17 00:00:00 2001
From: Peter Cooper <peter_cooper at apple.com>
Date: Tue, 6 Jan 2026 21:15:58 -0800
Subject: [PATCH 2/3] newlines
---
clang/test/CodeGen/stack-protector-vars.c | 2 +-
clang/test/Sema/no_stack_protector.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/CodeGen/stack-protector-vars.c b/clang/test/CodeGen/stack-protector-vars.c
index 218cdc33b3010..03ffb32f807d2 100644
--- a/clang/test/CodeGen/stack-protector-vars.c
+++ b/clang/test/CodeGen/stack-protector-vars.c
@@ -22,4 +22,4 @@ void test2(const char *msg) {
printf("%s\n", b);
}
-// CHECK: ![[A]] = !{i32 0}
\ No newline at end of file
+// CHECK: ![[A]] = !{i32 0}
diff --git a/clang/test/Sema/no_stack_protector.c b/clang/test/Sema/no_stack_protector.c
index fab3df667883a..d6b0c4d8bc208 100644
--- a/clang/test/Sema/no_stack_protector.c
+++ b/clang/test/Sema/no_stack_protector.c
@@ -10,4 +10,4 @@ void __attribute__((no_stack_protector(2))) bar(void) {} // expected-error {{'n
void func()
{
int __attribute__((no_stack_protector)) localvar;
-}
\ No newline at end of file
+}
>From 4ec8dfdc4ef035f121d088484c6f7d241e810994 Mon Sep 17 00:00:00 2001
From: Peter Cooper <peter_cooper at apple.com>
Date: Tue, 6 Jan 2026 21:32:49 -0800
Subject: [PATCH 3/3] Add new StackProtectorIgnore and StackProtectorIgnoreDocs
---
clang/include/clang/Basic/Attr.td | 9 ++++++++-
clang/include/clang/Basic/AttrDocs.td | 11 +++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 2a31626ca2471..525a883f5987c 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2726,11 +2726,18 @@ 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, LocalVar]>;
+ let Subjects = SubjectList<[Function]>;
let Documentation = [NoStackProtectorDocs];
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..79b872c6bbe78 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5736,6 +5736,17 @@ 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.
+
+If ``-fstack-protector-all`` is specified then the given function will still
+use a stack protector, even if some/all variables have this attribute.
+ }];
+}
+
def StrictGuardStackCheckDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
More information about the cfe-commits
mailing list