[clang] Allow the no_stack_protector attribute on local variables (PR #173311)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 13 23:14:17 PST 2026
https://github.com/cooperp updated https://github.com/llvm/llvm-project/pull/173311
>From 42b35a03777989c2da35ecd1d7cd06a970780f78 Mon Sep 17 00:00:00 2001
From: Peter Cooper <peter_cooper at apple.com>
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/lib/CodeGen/CGDecl.cpp | 8 +++
clang/test/CodeGen/stack-protector-vars.cpp | 70 +++++++++++++++++++
...a-attribute-supported-attributes-list.test | 1 +
clang/test/Sema/stack_protector_ignore.c | 12 ++++
7 files changed, 117 insertions(+)
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/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8b1cd83af2396..2d9278f599037 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1635,6 +1635,14 @@ 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}));
+ }
+ }
} else {
EnsureInsertPoint();
diff --git a/clang/test/CodeGen/stack-protector-vars.cpp b/clang/test/CodeGen/stack-protector-vars.cpp
new file mode 100644
index 0000000000000..7169174f79f06
--- /dev/null
+++ b/clang/test/CodeGen/stack-protector-vars.cpp
@@ -0,0 +1,70 @@
+// 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
+
+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];
+ 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;
+ 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;
+ 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;
+ 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..50a2f49c5edd1
--- /dev/null
+++ b/clang/test/Sema/stack_protector_ignore.c
@@ -0,0 +1,12 @@
+// 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}}
+ __attribute__((stack_protector_ignore)) var + var; // expected-error {{expected identifier or '('}}
+}
More information about the cfe-commits
mailing list