[clang] [llvm] [clang-cl] Use MSVC's "GS buffer" heuristic for the default /GS (PR #225626)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 23 00:53:22 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: Pan Tao (PanTao2)
<details>
<summary>Changes</summary>
clang-cl enables `/GS` (Buffer Security Check) by default, but implements it as `-fstack-protector-strong`. That heuristic protects considerably more functions than MSVC's does — most notably any function that merely takes the address of a local — which costs code size and performance relative to MSVC for no compatibility benefit.
This patch adds a new stack protector mode, `LangOptions::SSPMSVC`, which clang-cl now passes via `-stack-protector 4` instead of the "strong" level. It still lowers to the `sspstrong` function attribute, but additionally emits a `"stack-protector-gs-buffer"="true"` function attribute that tells `StackProtector` to select on MSVC's rules: a function is protected only if it takes no variable argument list and allocates a "GS buffer", i.e.
- an array larger than 4 bytes with more than two elements whose element type is not a pointer type,
- an aggregate larger than 8 bytes that contains no pointers,
- an `alloca()` buffer of any size, or
- an aggregate containing any of the above.
Also matching MSVC, no check is inserted at `-O0`. Stack layout rules are unchanged.
`/GS-`, `__declspec(safebuffers)` and `__declspec(strict_gs_check)` keep their existing meanings; `strict_gs_check` continues to select the broader "strong" heuristic and still applies at `-O0`, since it is an explicit per-function opt-in.
To keep inlining from weakening protection, `adjustCallerSSPLevel()` drops `"stack-protector-gs-buffer"` from the caller when a callee that was compiled with the stronger heuristic is inlined into it.
Because clang-cl no longer defaults to a GCC-compatible level, it no longer predefines `__SSP_STRONG__`; `/clang:-fstack-protector-strong` restores the previous behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
Patch is 29.51 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/225626.diff
15 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+13)
- (modified) clang/include/clang/Basic/LangOptions.def (+1-1)
- (modified) clang/include/clang/Basic/LangOptions.h (+1-1)
- (modified) clang/include/clang/Options/Options.td (+2-2)
- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+17-1)
- (modified) clang/lib/CodeGen/CodeGenModule.h (+2)
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+1-1)
- (added) clang/test/CodeGen/ms-stack-protector-gs-buffer.c (+48)
- (modified) clang/test/Driver/cl-options.c (+11-2)
- (modified) clang/test/Driver/cl-options.cu (+1-1)
- (modified) llvm/docs/LangRef.md (+24)
- (modified) llvm/lib/CodeGen/StackProtector.cpp (+113-9)
- (modified) llvm/lib/IR/Attributes.cpp (+9)
- (added) llvm/test/CodeGen/X86/stack-protector-gs-buffer.ll (+185)
- (added) llvm/test/Transforms/Inline/inline-ssp-gs-buffer.ll (+46)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index e5da258b9950a..24b8ee4f030aa 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -122,6 +122,19 @@ features cannot lower the translation-unit ABI level;
target triples (except 32b arm targets). Can be disabled via
`-fno-stack-clash-protection`.
+- clang-cl's default `/GS` (Buffer Security Check) now uses MSVC's "GS buffer"
+ heuristic instead of the GCC-compatible `-fstack-protector-strong` one. A
+ function is protected if it allocates an array larger than 4 bytes with more
+ than two elements of non-pointer type, a pointer-free aggregate larger than 8
+ bytes, an `_alloca` buffer, or an aggregate containing one of those. Matching
+ MSVC, a local merely having its address taken no longer protects a function,
+ varargs functions are never protected, and no protection is inserted when
+ optimizations are disabled. `/GS-` and `__declspec(safebuffers)` still
+ disable it, and `__declspec(strict_gs_check)` still selects the broader
+ heuristic. Since clang-cl no longer defaults to a GCC-compatible level it no
+ longer predefines `__SSP_STRONG__`; pass `/clang:-fstack-protector-strong`
+ for the previous behavior.
+
### Clang Python Bindings Potentially Breaking Changes
- `CompletionChunkKind` instance's `__str__` representation has been adapted to be consistent with other enums in the library.
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index d7637f2dfd507..0e7fc5a30b5c0 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -369,7 +369,7 @@ ENUM_LANGOPT(ExternDeclNoDLLStorageClassVisibility, VisibilityFromDLLStorageClas
LANGOPT(SemanticInterposition , 1, 0, Benign, "semantic interposition")
LANGOPT(HalfNoSemanticInterposition, 1, 0, Benign,
"Like -fno-semantic-interposition but don't use local aliases")
-ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff, NotCompatible,
+ENUM_LANGOPT(StackProtector, StackProtectorMode, 3, SSPOff, NotCompatible,
"stack protector mode")
ENUM_LANGOPT(TrivialAutoVarInit, TrivialAutoVarInitKind, 2, TrivialAutoVarInitKind::Uninitialized, Benign,
"trivial automatic variable initialization")
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 7539e000d03f9..2b80946009070 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -96,7 +96,7 @@ class LangOptionsBase {
};
enum GCMode { NonGC, GCOnly, HybridGC };
- enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq };
+ enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq, SSPMSVC };
// Automatic variables live on the stack, and when trivial they're usually
// uninitialized because it's undefined behavior to use them without
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index d4bf48040029f..4494a0ca8b15e 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -8984,9 +8984,9 @@ def static_define : Flag<["-"], "static-define">,
MarshallingInfoFlag<LangOpts<"Static">>;
def stack_protector : Separate<["-"], "stack-protector">,
HelpText<"Enable stack protectors">,
- Values<"0,1,2,3">,
+ Values<"0,1,2,3,4">,
NormalizedValuesScope<"LangOptions">,
- NormalizedValues<["SSPOff", "SSPOn", "SSPStrong", "SSPReq"]>,
+ NormalizedValues<["SSPOff", "SSPOn", "SSPStrong", "SSPReq", "SSPMSVC"]>,
MarshallingInfoEnum<LangOpts<"StackProtector">, "SSPOff">;
def stack_protector_buffer_size : Separate<["-"], "stack-protector-buffer-size">,
HelpText<"Lower bound for a buffer to be considered for stack protection">,
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 70445dd65ea2b..496efdb731fc0 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1163,12 +1163,24 @@ static bool isStackProtectorOn(const LangOptions &LangOpts,
return LangOpts.getStackProtector() == Mode;
}
+bool CodeGenModule::useMSVCGSBufferHeuristic(const Decl *D) const {
+ return isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPMSVC) &&
+ !(D && D->hasAttr<StrictGuardStackCheckAttr>());
+}
+
std::optional<llvm::Attribute::AttrKind>
CodeGenModule::StackProtectorAttribute(const Decl *D) const {
+ // MSVC does not insert buffer security checks when optimizations are
+ // disabled. __declspec(strict_gs_check) is an explicit per-function opt-in,
+ // so it still applies at -O0.
+ if (useMSVCGSBufferHeuristic(D) && CodeGenOpts.OptimizationLevel == 0)
+ return std::nullopt;
+
if (D && D->hasAttr<NoStackProtectorAttr>())
; // Do nothing.
else if (D && D->hasAttr<StrictGuardStackCheckAttr>() &&
- isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPOn))
+ (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPOn) ||
+ isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPMSVC)))
return llvm::Attribute::StackProtectStrong;
else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPOn))
return llvm::Attribute::StackProtect;
@@ -1176,6 +1188,8 @@ CodeGenModule::StackProtectorAttribute(const Decl *D) const {
return llvm::Attribute::StackProtectStrong;
else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq))
return llvm::Attribute::StackProtectReq;
+ else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPMSVC))
+ return llvm::Attribute::StackProtectStrong;
return std::nullopt;
}
@@ -3170,6 +3184,8 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
if (std::optional<llvm::Attribute::AttrKind> Attr =
StackProtectorAttribute(D)) {
B.addAttribute(*Attr);
+ if (useMSVCGSBufferHeuristic(D))
+ B.addAttribute("stack-protector-gs-buffer", "true");
}
if (!D) {
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 9e3f073c20f4a..02b5d5d96bdc0 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1994,6 +1994,8 @@ class CodeGenModule : public CodeGenTypeCache {
std::optional<llvm::Attribute::AttrKind>
StackProtectorAttribute(const Decl *D) const;
+ bool useMSVCGSBufferHeuristic(const Decl *D) const;
+
std::string getPFPFieldName(const FieldDecl *FD);
llvm::GlobalValue *getPFPDeactivationSymbol(const FieldDecl *FD);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 6636a5fd6e655..3511b5630d59b 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8894,7 +8894,7 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
if (!isNVPTX && Args.hasFlag(options::OPT__SLASH_GS, options::OPT__SLASH_GS_,
/*Default=*/true)) {
CmdArgs.push_back("-stack-protector");
- CmdArgs.push_back(Args.MakeArgString(Twine(LangOptions::SSPStrong)));
+ CmdArgs.push_back(Args.MakeArgString(Twine(LangOptions::SSPMSVC)));
}
const Driver &D = getToolChain().getDriver();
diff --git a/clang/test/CodeGen/ms-stack-protector-gs-buffer.c b/clang/test/CodeGen/ms-stack-protector-gs-buffer.c
new file mode 100644
index 0000000000000..aa536f1108932
--- /dev/null
+++ b/clang/test/CodeGen/ms-stack-protector-gs-buffer.c
@@ -0,0 +1,48 @@
+// Check the attributes clang-cl's default /GS emits: sspstrong paired with the
+// "stack-protector-gs-buffer" marker that selects MSVC's GS-buffer heuristic.
+//
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions -O2 \
+// RUN: -stack-protector 4 -emit-llvm %s -o - | FileCheck %s --check-prefix=GS
+//
+// MSVC does not insert buffer security checks when optimizations are disabled.
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions \
+// RUN: -stack-protector 4 -emit-llvm %s -o - | FileCheck %s --check-prefix=NOOPT
+//
+// An explicit GCC-style level is unaffected by any of this.
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions -O2 \
+// RUN: -stack-protector 2 -emit-llvm %s -o - | FileCheck %s --check-prefix=STRONG
+
+void use(void *);
+
+// GS: define dso_local void @plain() {{.*}}#[[#PLAIN:]] {
+// NOOPT: define dso_local void @plain() #[[#PLAIN:]] {
+// STRONG: define dso_local void @plain() {{.*}}#[[#SPLAIN:]] {
+void plain(void) { char buf[64]; use(buf); }
+
+// __declspec(safebuffers) opts out entirely, at every level.
+// GS: define dso_local void @safe() {{.*}}#[[#SAFE:]] {
+// STRONG: define dso_local void @safe() {{.*}}#[[#SSAFE:]] {
+__declspec(safebuffers) void safe(void) { char buf[64]; use(buf); }
+
+// __declspec(strict_gs_check) asks for a cookie in a greater number of
+// functions, so it opts back out of the narrower GS-buffer rules and is not
+// suppressed at -O0.
+// GS: define dso_local void @strict() {{.*}}#[[#STRICT:]] {
+// NOOPT: define dso_local void @strict() #[[#STRICT:]] {
+__declspec(strict_gs_check) void strict(void) { char buf[64]; use(buf); }
+
+// The string attribute sorts between "stack-protector-buffer-size" and
+// "target-features", so matching those neighbours proves it is absent.
+//
+// GS: attributes #[[#PLAIN]] = { nounwind sspstrong {{.*}}"stack-protector-buffer-size"="8" "stack-protector-gs-buffer"="true" "target-features"
+// GS: attributes #[[#SAFE]] = { nounwind "min-legal-vector-width"
+// GS: attributes #[[#STRICT]] = { nounwind sspstrong {{.*}}"stack-protector-buffer-size"="8" "target-features"
+
+// At -O0 the GS-buffer default produces no stack protector attribute at all,
+// so @plain shares an attribute group with @safe.
+// NOOPT: attributes #[[#PLAIN]] = { noinline nounwind optnone "min-legal-vector-width"
+// NOOPT: attributes #[[#STRICT]] = { noinline nounwind optnone sspstrong {{.*}}"stack-protector-buffer-size"="8" "target-features"
+
+// -stack-protector 2 keeps the GCC-compatible strong heuristic: no marker.
+// STRONG: attributes #[[#SPLAIN]] = { nounwind sspstrong {{.*}}"stack-protector-buffer-size"="8" "target-features"
+// STRONG: attributes #[[#SSAFE]] = { nounwind "min-legal-vector-width"
diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c
index 57d82622ef4de..4f9caa9d022e1 100644
--- a/clang/test/Driver/cl-options.c
+++ b/clang/test/Driver/cl-options.c
@@ -119,14 +119,23 @@
// Security Buffer Check is on by default.
// RUN: %clang_cl -### -- %s 2>&1 | FileCheck -check-prefix=GS-default %s
-// GS-default: "-stack-protector" "2"
+// GS-default: "-stack-protector" "4"
// RUN: %clang_cl /GS -### -- %s 2>&1 | FileCheck -check-prefix=GS %s
-// GS: "-stack-protector" "2"
+// GS: "-stack-protector" "4"
// RUN: %clang_cl /GS- -### -- %s 2>&1 | FileCheck -check-prefix=GS_ %s
// GS_-NOT: -stack-protector
+// An explicit GCC-style level is rendered after the /GS default and wins.
+// RUN: %clang_cl /clang:-fstack-protector-strong -### -- %s 2>&1 | FileCheck -check-prefix=GS-strong %s
+// GS-strong: "-stack-protector" "4"
+// GS-strong-SAME: "-stack-protector" "2"
+
+// RUN: %clang_cl /clang:-fstack-protector-all -### -- %s 2>&1 | FileCheck -check-prefix=GS-all %s
+// GS-all: "-stack-protector" "4"
+// GS-all-SAME: "-stack-protector" "3"
+
// RUN: %clang_cl /Gy -### -- %s 2>&1 | FileCheck -check-prefix=Gy %s
// Gy: -ffunction-sections
diff --git a/clang/test/Driver/cl-options.cu b/clang/test/Driver/cl-options.cu
index b241ec6672d85..4c6d4aa9756cb 100644
--- a/clang/test/Driver/cl-options.cu
+++ b/clang/test/Driver/cl-options.cu
@@ -8,7 +8,7 @@
// GS-default: "-cc1" "-triple" "nvptx{{(64)?}}-nvidia-cuda"
// GS-default-NOT: "-stack-protector"
// GS-default: "-cc1" "-triple"
-// GS-default: "-stack-protector" "2"
+// GS-default: "-stack-protector" "4"
// -exceptions should be passed to device-side compilation.
// RUN: not %clang_cl /c /GX -### -nocudalib -nocudainc -- %s 2>&1 | FileCheck -check-prefix=GX %s
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index a4816d337e02b..40d9183279a2b 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -2836,6 +2836,30 @@ fn -> other_fn -> other_fn ; fn is norecurse
function which has an `ssp` or `sspstrong` attribute, the calling
function's attribute will be upgraded to `sspreq`.
+`"stack-protector-gs-buffer"`
+: This attribute replaces the heuristic used by the `ssp` and `sspstrong`
+ attributes with the one MSVC's `/GS` (Buffer Security Check) option
+ uses. It has no effect on a function that also has `sspreq`. It takes a
+ boolean value, and is emitted by clang-cl.
+
+ Under this heuristic a function is protected if it takes no variable
+ argument list and it allocates a "GS buffer", which is any of:
+
+ - an array that is larger than 4 bytes, has more than two elements, and
+ has an element type that is not a pointer type;
+ - a data structure whose size is more than 8 bytes and that contains no
+ pointers;
+ - a buffer allocated by `alloca()`, regardless of size;
+ - any class or structure that contains a GS buffer.
+
+ Unlike `sspstrong`, a local variable merely having its address taken does
+ not cause a function to be protected. Stack layout rules are unchanged.
+
+ If a function with an `ssp` or `sspstrong` attribute but no
+ `"stack-protector-gs-buffer"` attribute is inlined into a calling function
+ that has one, the attribute is dropped from the caller, so that inlining
+ cannot weaken the callee's protection.
+
(strictfp)=
`strictfp`
diff --git a/llvm/lib/CodeGen/StackProtector.cpp b/llvm/lib/CodeGen/StackProtector.cpp
index 9ec1d0630952d..06158545e1215 100644
--- a/llvm/lib/CodeGen/StackProtector.cpp
+++ b/llvm/lib/CodeGen/StackProtector.cpp
@@ -63,6 +63,17 @@ static cl::opt<bool> EnableSelectionDAGSP("enable-selectiondag-sp",
static cl::opt<bool> DisableCheckNoReturn("disable-check-noreturn-call",
cl::init(false), cl::Hidden);
+/// Function attribute selecting MSVC's /GS (Buffer Security Check) heuristic.
+/// Emitted by clang-cl.
+static constexpr const char *GSBufferAttr = "stack-protector-gs-buffer";
+
+/// An array is only an MSVC "GS buffer" if it is strictly larger than this.
+static constexpr uint64_t MinGSArraySize = 4;
+
+/// A pointer-free aggregate is only an MSVC "GS buffer" if it is strictly
+/// larger than this.
+static constexpr uint64_t MinGSAggregateSize = 8;
+
/// InsertStackProtectors - Insert code into the prologue and epilogue of the
/// function.
///
@@ -275,6 +286,75 @@ static bool ContainsProtectableArray(Type *Ty, Module *M, unsigned SSPBufferSize
return NeedsProtector;
}
+/// Returns true if \p Ty holds a pointer anywhere within it. MSVC's GS-buffer
+/// rules treat a pointer-free aggregate as something that may be used as a
+/// buffer, so the presence of a pointer is what disqualifies it.
+static bool ContainsPointer(Type *Ty) {
+ if (Ty->isPointerTy())
+ return true;
+ if (auto *AT = dyn_cast<ArrayType>(Ty))
+ return ContainsPointer(AT->getElementType());
+ if (auto *ST = dyn_cast<StructType>(Ty))
+ return any_of(ST->elements(), ContainsPointer);
+ return false;
+}
+
+/// Returns true if \p Ty is, or contains, a "GS buffer" as defined by MSVC's
+/// /GS (Buffer Security Check) documentation:
+///
+/// - an array that is larger than 4 bytes, has more than two elements, and
+/// has an element type that is not a pointer type;
+/// - a data structure whose size is more than 8 bytes and that contains no
+/// pointers;
+/// - any class or structure that contains a GS buffer.
+///
+/// \param [out] IsLarge is set to true if the GS buffer that was found is
+/// "large" (>= ssp-buffer-size), so that it is laid out closest to the stack
+/// guard. In an aggregate holding several GS buffers this is set if any of
+/// them is large.
+static bool ContainsGSBuffer(Type *Ty, Module *M, unsigned SSPBufferSize,
+ bool &IsLarge) {
+ if (!Ty)
+ return false;
+
+ const DataLayout &DL = M->getDataLayout();
+ auto Found = [&](Type *BufferTy) {
+ if (DL.getTypeAllocSize(BufferTy).getKnownMinValue() >= SSPBufferSize)
+ IsLarge = true;
+ return true;
+ };
+
+ if (auto *AT = dyn_cast<ArrayType>(Ty)) {
+ if (!AT->getElementType()->isPointerTy() && AT->getNumElements() > 2 &&
+ DL.getTypeAllocSize(AT).getKnownMinValue() > MinGSArraySize)
+ return Found(AT);
+
+ // The array itself is not a GS buffer, but its elements may still be or
+ // contain one, e.g. an array of two structs that each hold a buffer.
+ return ContainsGSBuffer(AT->getElementType(), M, SSPBufferSize, IsLarge);
+ }
+
+ auto *ST = dyn_cast<StructType>(Ty);
+ if (!ST || ST->isOpaque())
+ return false;
+
+ if (DL.getTypeAllocSize(ST).getKnownMinValue() > MinGSAggregateSize &&
+ !ContainsPointer(ST))
+ return Found(ST);
+
+ bool NeedsProtector = false;
+ for (Type *ET : ST->elements())
+ if (ContainsGSBuffer(ET, M, SSPBufferSize, IsLarge)) {
+ // If a member is a large GS buffer then we are done. Otherwise keep
+ // looking, in case a later member is a large one.
+ if (IsLarge)
+ return true;
+ NeedsProtector = true;
+ }
+
+ return NeedsProtector;
+}
+
/// Maximum remaining allocation size observed for a phi node, and how often
/// the allocation size has already been decreased. We only allow a limited
/// number of decreases.
@@ -404,7 +484,8 @@ static const CallInst *findStackProtectorIntrinsic(Function &F) {
/// Check whether or not this function needs a stack protector based
/// upon the stack protector level.
///
-/// We use two heuristics: a standard (ssp) and strong (sspstrong).
+/// We use three heuristics: a standard (ssp), a strong (sspstrong) and an
+/// MSVC-compatible one selected by the "stack-protector-gs-buffer" attribute.
/// The standard heuristic which will add a guard variable to functions that
/// call alloca with a either a variable size or a size >= SSPBufferSize,
/// functions with character buffers larger than SSPBufferSize, and functions
@@ -413,12 +494,17 @@ static const CallInst *findStackProtectorIntrinsic(Function &F) {
/// regardless of size, functions with any buffer regardless of type and size,
/// functions with aggregates that contain any buffer regardless of type and
/// size, and functions that contain stack-based variables that have had their
-/// address taken.
+/// address taken. The MSVC heuristic implements /GS (Buffer Security Check):
+/// it adds a guard variable to functions that call alloca regardless of size
+/// and to functions holding a "GS buffer" (see ContainsGSBuffer), but it does
+/// not consider a variable's address being taken, and it never protects a
+/// function that takes a variable argument list.
bool SSPLayoutAnalysis::requiresStackProtector(Function *F,
SSPLayoutMap *Layout) {
Module *M = F->getParent();
bool Strong = false;
bool NeedsProtector = false;
+ bool GSBuffer = false;
// The set of PHI nodes visited when determining if a variable's reference has
// been taken. This set is maintained to ensure we don't visit the same PHI
@@ -447,9 +533,20 @@ bool SSPLayoutAnalysis::requiresStackProtector(Function *F,
});
NeedsProtector = true;
Strong = true; // Use the same heuristic as strong to determine SSPLayout
- } else if (F->hasFnAttribute(Attribute::StackProtectStrong))
- Strong = true;
- else if (!F->hasFnAttribute(Attribute::StackProtect))
+ } else if (F->hasFnAttribute(Attribute::StackProtectStrong)) {
+ // clang-cl's default /GS asks for MSVC's GS-buffer rules rather than the
+ // GCC-compatible strong heuristic. An explicit sspreq (handled above)
+ // still wins over both.
+ GSBuffer = F->getFnAttribute(GSBufferAttr).getValueAsBool();
+ Strong = !GSBuffer;
+ } else if (F->hasFnAttribute(Attribute::StackProtect)) {
+ GSBuffer = F->getFnAttribute(GSBufferAttr).getValueAsBool();
+ } else {
+ return false;
+ }
+
+ // MSVC's /GS does not protect functions that take a variable argument list.
+ if (GSBuffer && F->isVarArg())
return false;
for (const BasicBlock...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/225626
More information about the llvm-commits
mailing list