https://github.com/amosher-nvidia created https://github.com/llvm/llvm-project/pull/231651
Add a way to apply only the changes to the stack frame layout that are made when stack protectors are enabled, without actually inserting a stack protector. This is useful on architectures which have hardware features that can act like stack protectors like Zicfiss on RISC-V, where any return addresses spilled to the stack effectively act as stack cookies. Changes to LLVM: Add a new "stack-protector-layout-only" function attribute. When specified, the StackProtector pass still identifies the objects that are likely to be overflowed, and adjusts the stack frame layout accordingly, but does not insert a stack protector. Add a new MachineFrameInfo flag, stackProtectorLayoutOnly. It tells PrologEpilogInserter and LocalStackSlotAllocation to apply the stack protector's frame layout changes for those objects although no stack protector object exists. Changes to Clang: Add a new flag -fstack-protector-layout-only which adds the layout-only attribute to every function that gets a stack protector attribute. At a minimum -fstack-protector is enabled so that the layout changes can be made; if stack protection would otherwise be off, by default or via an explicit -fno-stack-protector, a warning is emitted. >From 42f9153ab11172ff65310de3946b0f3eb230fce9 Mon Sep 17 00:00:00 2001 From: Avery Mosher <[email protected]> Date: Fri, 18 Sep 2026 18:13:38 -0700 Subject: [PATCH] [CodeGen][Clang] Add -fstack-protector-layout-only Add a way to apply only the changes to the stack frame layout that are made when stack protectors are enabled, without actually inserting a stack protector. LLVM: Add a new "stack-protector-layout-only" function attribute. When specified, the StackProtector pass still identifies the objects that are likely to be overflowed, and adjusts the stack frame layout accordingly, but does not insert a stack protector. Add a new MachineFrameInfo flag, stackProtectorLayoutOnly. It tells PrologEpilogInserter and LocalStackSlotAllocation to apply the stack protector's frame layout changes for those objects although no stack protector object exists. Clang: Add a new flag -fstack-protector-layout-only which adds the layout-only attribute to every function that gets a stack protector attribute. At a minimum -fstack-protector is enabled so that the layout changes can be made; if stack protection would otherwise be off, by default or via an explicit -fno-stack-protector, a warning is emitted. --- clang/docs/ReleaseNotes.md | 6 + clang/docs/UsersManual.md | 11 ++ clang/include/clang/Basic/CodeGenOptions.def | 1 + clang/include/clang/Options/Options.td | 7 + clang/lib/CodeGen/CodeGenModule.cpp | 2 + clang/lib/Driver/ToolChains/Clang.cpp | 19 +++ clang/test/CodeGen/stack-protector.c | 9 ++ clang/test/Driver/stack-protector.c | 32 ++++ llvm/docs/LangRef.md | 9 ++ llvm/docs/ReleaseNotes.md | 8 + llvm/include/llvm/CodeGen/MIRYamlMapping.h | 4 + llvm/include/llvm/CodeGen/MachineFrameInfo.h | 11 ++ llvm/include/llvm/CodeGen/StackProtector.h | 3 + llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 3 +- llvm/lib/CodeGen/LocalStackSlotAllocation.cpp | 31 ++-- llvm/lib/CodeGen/MIRParser/MIRParser.cpp | 1 + llvm/lib/CodeGen/MIRPrinter.cpp | 1 + llvm/lib/CodeGen/PrologEpilogInserter.cpp | 43 +++--- llvm/lib/CodeGen/StackProtector.cpp | 32 ++++ .../CodeGen/TargetLoweringObjectFileImpl.cpp | 3 +- .../stack-protector-layout-only-codegen.ll | 140 ++++++++++++++++++ .../AArch64/stack-protector-layout-only.ll | 93 ++++++++++++ llvm/test/CodeGen/MIR/Generic/frame-info.mir | 3 + .../stack-protector-layout-only-codegen.ll | 120 +++++++++++++++ .../RISCV/stack-protector-layout-only.ll | 94 ++++++++++++ .../stack-protector-layout-only-funclet.ll | 37 +++++ .../X86/stack-protector-layout-only.ll | 94 ++++++++++++ .../Inline/inline_ssp_layout_only.ll | 86 +++++++++++ .../StackProtector/layout-only-no-analysis.ll | 14 ++ .../layout-only-no-protector.ll | 54 +++++++ llvm/tools/llvm-reduce/ReducerWorkItem.cpp | 2 + 31 files changed, 938 insertions(+), 35 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll create mode 100644 llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll create mode 100644 llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll create mode 100644 llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll create mode 100644 llvm/test/CodeGen/X86/stack-protector-layout-only-funclet.ll create mode 100644 llvm/test/CodeGen/X86/stack-protector-layout-only.ll create mode 100644 llvm/test/Transforms/Inline/inline_ssp_layout_only.ll create mode 100644 llvm/test/Transforms/StackProtector/layout-only-no-analysis.ll create mode 100644 llvm/test/Transforms/StackProtector/layout-only-no-protector.ll diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 1dc1764978ea2..23f30097f5985 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -290,6 +290,12 @@ features cannot lower the translation-unit ABI level; - New option `-fdefined-pointer-subtraction` added to preserve stable semantics when subtracting pointers to unrelated objects. +- Added `-fstack-protector-layout-only`. When specified, changes to the layout + of a stack frame that occur when `-fstack-protector` is specified are applied + but a stack protector is not actually inserted. This allows targets that have + features that can act as pseudo-stack-protectors, like RISC-V's shadow stack, + to benefit from the protection added by reordering stack variables. + - Added `--print-cxx-stdlib` and `--print-cxx-stdlib-include-dirs` to print the C++ standard library selected by the driver and the include directories added for it. diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md index 96d1c4e009c71..a310890a58fcb 100644 --- a/clang/docs/UsersManual.md +++ b/clang/docs/UsersManual.md @@ -3037,6 +3037,17 @@ at block entry, which is more expensive and no more useful for the jumps this feature exists to guard against. Clang applies the C++ rule in both languages. ::: +:::{option} -f[no-]stack-protector-layout-only + +When specified, stack protectors will not be inserted, but stack variables will +continue to be rearranged as if they were. This allows targets that have +features that can act as pseudo-stack-protectors, like RISC-V's shadow stack, +to benefit from the protection added by reordering stack variables. + +This option depends on at least `-fstack-protector`, so it will be overridden to +being enabled with a warning, even if `-fno-stack-protector` is given. +::: + (strict_aliasing)= (strict-aliasing)= diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 10d9a4e3a6a82..fbe34acf667e5 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -167,6 +167,7 @@ CODEGENOPT(CallFEntry , 1, 0, Benign) ///< Set when -mfentry is enabled. CODEGENOPT(MNopMCount , 1, 0, Benign) ///< Set when -mnop-mcount is enabled. CODEGENOPT(RecordMCount , 1, 0, Benign) ///< Set when -mrecord-mcount is enabled. CODEGENOPT(StackProtectorGuardRecord, 1, 0, Benign) ///< Set when -mstack-protector-guard-record is enabled. +CODEGENOPT(StackProtectorLayoutOnly, 1, 0, Benign) ///< Set when -fstack-protector-layout-only is enabled. CODEGENOPT(PackedStack , 1, 0, Benign) ///< Set when -mpacked-stack is enabled. CODEGENOPT(LessPreciseFPMAD , 1, 0, Benign) ///< Enable less precise MAD instructions to ///< be generated. diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 1adc5f8399e4b..7d0ea985436c5 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -4626,6 +4626,13 @@ defm split_stack : BoolFOption<"split-stack", PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use segmented stack">>; def fstack_protector_all : Flag<["-"], "fstack-protector-all">, Group<f_Group>, HelpText<"Enable stack protectors for all functions">; +defm stack_protector_layout_only : BoolFOption<"stack-protector-layout-only", + CodeGenOpts<"StackProtectorLayoutOnly">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption, CC1Option], + "Apply only the stack frame layout changes as part of stack protection, " + "without inserting stack protectors">, + NegFlag<SetFalse, [], [ClangOption], + "Insert stack protectors when stack protection is enabled">>; defm stack_clash_protection : BoolFOption<"stack-clash-protection", CodeGenOpts<"StackClashProtector">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">, diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index bce2106a74988..a7caf8abf7e04 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3186,6 +3186,8 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, if (std::optional<llvm::Attribute::AttrKind> Attr = StackProtectorAttribute(D)) { B.addAttribute(*Attr); + if (CodeGenOpts.StackProtectorLayoutOnly) + B.addAttribute("stack-protector-layout-only"); } if (!D) { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index fb6effb65d71f..20adb4d67f30f 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3673,10 +3673,29 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, StackProtectorLevel = DefaultStackProtectorLevel; } + bool LayoutOnly = false; + if (Arg *A = Args.getLastArg(options::OPT_fstack_protector_layout_only, + options::OPT_fno_stack_protector_layout_only)) { + LayoutOnly = + A->getOption().matches(options::OPT_fstack_protector_layout_only); + if (LayoutOnly && EffectiveTriple.isBPF()) { + D.Diag(diag::warn_drv_unsupported_option_for_target) + << A->getSpelling() << EffectiveTriple.getTriple(); + LayoutOnly = false; + } + if (LayoutOnly && StackProtectorLevel == LangOptions::SSPOff) { + D.Diag(diag::warn_drv_overriding_option) + << "-fno-stack-protector" << A->getSpelling(); + StackProtectorLevel = LangOptions::SSPOn; + } + } + if (StackProtectorLevel) { CmdArgs.push_back("-stack-protector"); CmdArgs.push_back(Args.MakeArgString(Twine(StackProtectorLevel))); } + if (LayoutOnly) + CmdArgs.push_back("-fstack-protector-layout-only"); // --param ssp-buffer-size= for (const Arg *A : Args.filtered(options::OPT__param)) { diff --git a/clang/test/CodeGen/stack-protector.c b/clang/test/CodeGen/stack-protector.c index 9b93f775ffe79..b8996c02ff33b 100644 --- a/clang/test/CodeGen/stack-protector.c +++ b/clang/test/CodeGen/stack-protector.c @@ -9,6 +9,11 @@ // RUN: %clang_cc1 -emit-llvm -o - %s -fsanitize=safe-stack -stack-protector 2 | FileCheck -check-prefix=DEF -check-prefix=SAFESTACK-SSPSTRONG %s // RUN: %clang_cc1 -emit-llvm -o - %s -fsanitize=safe-stack -stack-protector 3 | FileCheck -check-prefix=DEF -check-prefix=SAFESTACK-SSPREQ %s +// RUN: %clang_cc1 -emit-llvm -o - %s -stack-protector 0 -fstack-protector-layout-only | FileCheck -check-prefix=DEF -check-prefix=NOSSP -check-prefix=NO-LAYOUT-ONLY %s +// RUN: %clang_cc1 -emit-llvm -o - %s -stack-protector 1 -fstack-protector-layout-only | FileCheck -check-prefix=DEF -check-prefix=SSP -check-prefix=LAYOUT-ONLY %s +// RUN: %clang_cc1 -emit-llvm -o - %s -stack-protector 2 -fstack-protector-layout-only | FileCheck -check-prefix=DEF -check-prefix=SSPSTRONG -check-prefix=LAYOUT-ONLY %s +// RUN: %clang_cc1 -emit-llvm -o - %s -stack-protector 3 -fstack-protector-layout-only | FileCheck -check-prefix=DEF -check-prefix=SSPREQ -check-prefix=LAYOUT-ONLY %s + typedef __SIZE_TYPE__ size_t; int printf(const char * _Format, ...); @@ -34,6 +39,7 @@ void test2(const char *msg) { // SSP: attributes #[[A]] = {{.*}} ssp{{ }} // SSPSTRONG: attributes #[[A]] = {{.*}} sspstrong // SSPREQ: attributes #[[A]] = {{.*}} sspreq +// LAYOUT-ONLY-SAME: "stack-protector-layout-only" // SAFESTACK-NOSSP: attributes #[[A]] = {{.*}} safestack // SAFESTACK-NOSSP-NOT: ssp @@ -53,3 +59,6 @@ void test2(const char *msg) { // SAFESTACK-SSPSTRONG-NOT: attributes #[[B]] = {{.*}} safestack sspstrong // SAFESTACK-SSPREQ: attributes #[[B]] = {{.*}} safestack // SAFESTACK-SSPREQ-NOT: attributes #[[B]] = {{.*}} safestack sspreq + +// LAYOUT-ONLY-NOT: attributes #[[B]] = {{.*}} "stack-protector-layout-only" +// NO-LAYOUT-ONLY-NOT: "stack-protector-layout-only" diff --git a/clang/test/Driver/stack-protector.c b/clang/test/Driver/stack-protector.c index e082b3361d058..f258ada07d162 100644 --- a/clang/test/Driver/stack-protector.c +++ b/clang/test/Driver/stack-protector.c @@ -55,3 +55,35 @@ // RUN: %clang -ffreestanding -target x86_64-apple-darwin10 -mmacos-version-min=10.6 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_MACOSX_10_6_KERNEL // SSP_MACOSX_10_6_KERNEL: "-stack-protector" "1" +// Test -fstack-protector-layout-only with the different stack protector levels + +// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY +// SSP-LAYOUT-ONLY: warning: overriding '-fno-stack-protector' option with '-fstack-protector-layout-only' +// SSP-LAYOUT-ONLY: "-stack-protector" "1" "-fstack-protector-layout-only" + +// RUN: %clang --target=x86_64-unknown-linux-gnu -fno-stack-protector -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-NO-SSP +// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-layout-only -fno-stack-protector -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-NO-SSP +// SSP-LAYOUT-ONLY-NO-SSP: warning: overriding '-fno-stack-protector' option with '-fstack-protector-layout-only' +// SSP-LAYOUT-ONLY-NO-SSP: "-stack-protector" "1" "-fstack-protector-layout-only" + +// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-layout-only -fstack-protector-strong -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-STRONG +// SSP-LAYOUT-ONLY-STRONG-NOT: warning: +// SSP-LAYOUT-ONLY-STRONG: "-stack-protector" "2" "-fstack-protector-layout-only" + +// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-all -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-ALL +// SSP-LAYOUT-ONLY-ALL-NOT: warning: +// SSP-LAYOUT-ONLY-ALL: "-stack-protector" "3" "-fstack-protector-layout-only" + +// RUN: %clang --target=i386-pc-openbsd -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-OPENBSD +// SSP-LAYOUT-ONLY-OPENBSD-NOT: warning: +// SSP-LAYOUT-ONLY-OPENBSD: "-stack-protector" "2" "-fstack-protector-layout-only" + +// RUN: %clang --target=x86_64-unknown-linux-gnu -fstack-protector-layout-only -fno-stack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=NO-SSP-LAYOUT-ONLY +// NO-SSP-LAYOUT-ONLY-NOT: "-stack-protector" +// NO-SSP-LAYOUT-ONLY-NOT: "-fstack-protector-layout-only" + +// RUN: %clang --target=bpf -fstack-protector-layout-only -### %s 2>&1 | FileCheck %s -check-prefix=SSP-LAYOUT-ONLY-BPF +// SSP-LAYOUT-ONLY-BPF: warning: ignoring '-fstack-protector-layout-only' option as it is not currently supported for target 'bpf' +// SSP-LAYOUT-ONLY-BPF-NOT: overriding +// SSP-LAYOUT-ONLY-BPF-NOT: "-stack-protector" +// SSP-LAYOUT-ONLY-BPF-NOT: "-fstack-protector-layout-only" diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md index 84af4a727c223..82ddd5c6db533 100644 --- a/llvm/docs/LangRef.md +++ b/llvm/docs/LangRef.md @@ -2841,6 +2841,15 @@ 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-layout-only"` +: This attribute is only meaningful in conjunction with one of the stack + protector attributes above. It indicates that only the changes to the + stack frame layout that are made for a stack protector are applied, + and no stack protectors are actually inserted. + + If a function with this attribute is inlined into a calling function, the + attribute will not be added to the calling function. + (strictfp)= `strictfp` diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index cec21851f0d76..90c76bb84e62d 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -172,6 +172,10 @@ Makes programs 10x faster by doing Special New Thing. * Added the `bitinsert` and `bitextract` instructions for bit-range manipulation on byte type values. +* Added the `"stack-protector-layout-only"` attribute. When specified, the + `ssp`/`sspstrong`/`sspreq` attributes still make changes to the layout of the + stack frame, but do not actually insert any stack protectors. + ### Changes to LLVM infrastructure * Removed `TargetOptions::FloatABIType`. The soft float ABI should be @@ -387,6 +391,10 @@ Makes programs 10x faster by doing Special New Thing. ### Changes to the CodeGen infrastructure +* `MachineFrameInfo` gained a `stackProtectorLayoutOnly` flag. It tells the + prolog/epilog inserter and local stack slot allocation to lay out the stack + objects as if the function had a stack protector, even though it does not. + * Fixed a crash ([#214750](https://github.com/llvm/llvm-project/issues/214750)) when compiling a function containing a static alloca of `(size_t)-1` bytes, whose diff --git a/llvm/include/llvm/CodeGen/MIRYamlMapping.h b/llvm/include/llvm/CodeGen/MIRYamlMapping.h index ab1c91d9f0de5..2d09ad29aedac 100644 --- a/llvm/include/llvm/CodeGen/MIRYamlMapping.h +++ b/llvm/include/llvm/CodeGen/MIRYamlMapping.h @@ -736,6 +736,7 @@ struct MachineFrameInfo { bool HasCalls = false; FramePointerKind FramePointerPolicy = FramePointerKind::None; StringValue StackProtector; + bool StackProtectorLayoutOnly = false; StringValue FunctionContext; unsigned MaxCallFrameSize = ~0u; ///< ~0u means: not computed yet. unsigned CVBytesOfCalleeSavedRegisters = 0; @@ -759,6 +760,7 @@ struct MachineFrameInfo { AdjustsStack == Other.AdjustsStack && HasCalls == Other.HasCalls && FramePointerPolicy == Other.FramePointerPolicy && StackProtector == Other.StackProtector && + StackProtectorLayoutOnly == Other.StackProtectorLayoutOnly && FunctionContext == Other.FunctionContext && MaxCallFrameSize == Other.MaxCallFrameSize && CVBytesOfCalleeSavedRegisters == @@ -788,6 +790,8 @@ template <> struct MappingTraits<MachineFrameInfo> { YamlIO.mapOptional("framePointerPolicy", MFI.FramePointerPolicy); YamlIO.mapOptional("stackProtector", MFI.StackProtector, StringValue()); // Don't print it out when it's empty. + YamlIO.mapOptional("stackProtectorLayoutOnly", MFI.StackProtectorLayoutOnly, + false); YamlIO.mapOptional("functionContext", MFI.FunctionContext, StringValue()); // Don't print it out when it's empty. YamlIO.mapOptional("maxCallFrameSize", MFI.MaxCallFrameSize, (unsigned)~0); diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h index 30699c3c18f1c..67308abe03667 100644 --- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h +++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h @@ -291,6 +291,10 @@ class MachineFrameInfo { /// The frame index for the stack protector. int StackProtectorIdx = -1; + /// True if the stack objects should be laid out as if the function had a + /// stack protector, even though it does not. + bool StackProtectorLayoutOnly = false; + /// The frame index for the function context. Used for SjLj exceptions. int FunctionContextIdx = -1; @@ -379,6 +383,13 @@ class MachineFrameInfo { void setStackProtectorIndex(int I) { StackProtectorIdx = I; } bool hasStackProtectorIndex() const { return StackProtectorIdx != -1; } + /// Return true if the stack objects are laid out as if the function had a + /// stack protector, even though it does not. + bool hasStackProtectorLayoutOnly() const { return StackProtectorLayoutOnly; } + void setStackProtectorLayoutOnly(bool B = true) { + StackProtectorLayoutOnly = B; + } + /// Return the index for the function context object. /// This object is used for SjLj exceptions. int getFunctionContextIndex() const { return FunctionContextIdx; } diff --git a/llvm/include/llvm/CodeGen/StackProtector.h b/llvm/include/llvm/CodeGen/StackProtector.h index 2d6dc76a65fe8..d627ae111322d 100644 --- a/llvm/include/llvm/CodeGen/StackProtector.h +++ b/llvm/include/llvm/CodeGen/StackProtector.h @@ -52,6 +52,9 @@ class SSPLayoutInfo { bool RequireStackProtector = false; + // Changes to frame layout are made, but no stack protector is inserted. + bool LayoutOnly = false; + // A prologue is generated. bool HasPrologue = false; diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index 9cf268ce8ce4f..33b99587ce027 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -1561,7 +1561,8 @@ void CodeViewDebug::beginFunctionImpl(const MachineFunction *MF) { GV.hasFnAttribute(Attribute::StackProtectReq)) { FPO |= FrameProcedureOptions::StrictSecurityChecks; } - } else if (!GV.hasStackProtectorFnAttr()) { + } else if (!GV.hasStackProtectorFnAttr() || + GV.hasFnAttribute("stack-protector-layout-only")) { // __declspec(safebuffers) disables stack guards. FPO |= FrameProcedureOptions::SafeBuffers; } diff --git a/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp b/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp index a388413525c23..697b9bae707b3 100644 --- a/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp +++ b/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp @@ -218,27 +218,30 @@ void LocalStackSlotImpl::calculateFrameObjectOffsets(MachineFunction &Fn) { Align MaxAlign; // Make sure that the stack protector comes before the local variables on the - // stack. + // stack. With StackProtectorLayoutOnly these objects are ordered the same + // way although no stack protector is inserted. SmallSet<int, 16> ProtectedObjs; - if (MFI.hasStackProtectorIndex()) { + if (MFI.hasStackProtectorIndex() || MFI.hasStackProtectorLayoutOnly()) { int StackProtectorFI = MFI.getStackProtectorIndex(); - // We need to make sure we didn't pre-allocate the stack protector when - // doing this. - // If we already have a stack protector, this will re-assign it to a slot - // that is **not** covering the protected objects. - assert(!MFI.isObjectPreAllocated(StackProtectorFI) && - "Stack protector pre-allocated in LocalStackSlotAllocation"); - StackObjSet LargeArrayObjs; StackObjSet SmallArrayObjs; StackObjSet AddrOfObjs; - // Only place the stack protector in the local stack area if the target - // allows it. - if (TFI.isStackIdSafeForLocalArea(MFI.getStackID(StackProtectorFI))) - AdjustStackOffset(MFI, StackProtectorFI, Offset, StackGrowsDown, - MaxAlign); + if (MFI.hasStackProtectorIndex()) { + // We need to make sure we didn't pre-allocate the stack protector when + // doing this. + // If we already have a stack protector, this will re-assign it to a slot + // that is **not** covering the protected objects. + assert(!MFI.isObjectPreAllocated(StackProtectorFI) && + "Stack protector pre-allocated in LocalStackSlotAllocation"); + + // Only place the stack protector in the local stack area if the target + // allows it. + if (TFI.isStackIdSafeForLocalArea(MFI.getStackID(StackProtectorFI))) + AdjustStackOffset(MFI, StackProtectorFI, Offset, StackGrowsDown, + MaxAlign); + } // Assign large stack objects first. for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) { diff --git a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp index 26af6ef073082..1c441f074a666 100644 --- a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp +++ b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp @@ -958,6 +958,7 @@ bool MIRParserImpl::initializeFrameInfo(PerFunctionMIParsingState &PFS, MFI.setHasCalls(YamlMFI.HasCalls); if (YamlMFI.FramePointerPolicy != FramePointerKind::None) MFI.setFramePointerPolicy(YamlMFI.FramePointerPolicy); + MFI.setStackProtectorLayoutOnly(YamlMFI.StackProtectorLayoutOnly); if (YamlMFI.MaxCallFrameSize != ~0u) MFI.setMaxCallFrameSize(YamlMFI.MaxCallFrameSize); MFI.setCVBytesOfCalleeSavedRegisters(YamlMFI.CVBytesOfCalleeSavedRegisters); diff --git a/llvm/lib/CodeGen/MIRPrinter.cpp b/llvm/lib/CodeGen/MIRPrinter.cpp index def751e6df6bb..87bf7af1cddf4 100644 --- a/llvm/lib/CodeGen/MIRPrinter.cpp +++ b/llvm/lib/CodeGen/MIRPrinter.cpp @@ -384,6 +384,7 @@ static void convertMFI(ModuleSlotTracker &MST, yaml::MachineFrameInfo &YamlMFI, YamlMFI.AdjustsStack = MFI.adjustsStack(); YamlMFI.HasCalls = MFI.hasCalls(); YamlMFI.FramePointerPolicy = MFI.getFramePointerPolicy(); + YamlMFI.StackProtectorLayoutOnly = MFI.hasStackProtectorLayoutOnly(); YamlMFI.MaxCallFrameSize = MFI.isMaxCallFrameSizeComputed() ? MFI.getMaxCallFrameSize() : ~0u; YamlMFI.CVBytesOfCalleeSavedRegisters = diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp index 49fa0f89d616c..ef0c36398fbc4 100644 --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -972,9 +972,12 @@ void PEIImpl::calculateFrameObjectOffsets(MachineFunction &MF) { EHRegNodeFrameIndex = FuncInfo->EHRegNodeFrameIndex; // Make sure that the stack protector comes before the local variables on the - // stack. + // stack. With StackProtectorLayoutOnly these objects are ordered the same + // way although no stack protector is inserted. SmallSet<int, 16> ProtectedObjs; - if (MFI.hasStackProtectorIndex()) { + bool DoSSPLayout = + MFI.hasStackProtectorIndex() || MFI.hasStackProtectorLayoutOnly(); + if (DoSSPLayout) { int StackProtectorFI = MFI.getStackProtectorIndex(); StackObjSet LargeArrayObjs; StackObjSet SmallArrayObjs; @@ -984,21 +987,23 @@ void PEIImpl::calculateFrameObjectOffsets(MachineFunction &MF) { // LocalStackSlotPass didn't already allocate a slot for it. // If we are told to use the LocalStackAllocationBlock, the stack protector // is expected to be already pre-allocated. - if (MFI.getStackID(StackProtectorFI) != TargetStackID::Default) { - // If the stack protector isn't on the default stack then it's up to the - // target to set the stack offset. - assert(MFI.getObjectOffset(StackProtectorFI) != 0 && - "Offset of stack protector on non-default stack expected to be " - "already set."); - assert(!MFI.isObjectPreAllocated(MFI.getStackProtectorIndex()) && - "Stack protector on non-default stack expected to not be " - "pre-allocated by LocalStackSlotPass."); - } else if (!MFI.getUseLocalStackAllocationBlock()) { - AdjustStackOffset(MFI, StackProtectorFI, StackGrowsDown, Offset, - MaxAlign); - } else if (!MFI.isObjectPreAllocated(MFI.getStackProtectorIndex())) { - llvm_unreachable( - "Stack protector not pre-allocated by LocalStackSlotPass."); + if (MFI.hasStackProtectorIndex()) { + if (MFI.getStackID(StackProtectorFI) != TargetStackID::Default) { + // If the stack protector isn't on the default stack then it's up to the + // target to set the stack offset. + assert(MFI.getObjectOffset(StackProtectorFI) != 0 && + "Offset of stack protector on non-default stack expected to be " + "already set."); + assert(!MFI.isObjectPreAllocated(MFI.getStackProtectorIndex()) && + "Stack protector on non-default stack expected to not be " + "pre-allocated by LocalStackSlotPass."); + } else if (!MFI.getUseLocalStackAllocationBlock()) { + AdjustStackOffset(MFI, StackProtectorFI, StackGrowsDown, Offset, + MaxAlign); + } else if (!MFI.isObjectPreAllocated(MFI.getStackProtectorIndex())) { + llvm_unreachable( + "Stack protector not pre-allocated by LocalStackSlotPass."); + } } // Assign large stack objects first. @@ -1091,8 +1096,8 @@ void PEIImpl::calculateFrameObjectOffsets(MachineFunction &MF) { // optimizing. BitVector StackBytesFree; if (!ObjectsToAllocate.empty() && - MF.getTarget().getOptLevel() != CodeGenOptLevel::None && - MFI.getStackProtectorIndex() < 0 && TFI.enableStackSlotScavenging(MF)) + MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !DoSSPLayout && + TFI.enableStackSlotScavenging(MF)) computeFreeStackSlots(MFI, StackGrowsDown, FixedCSEnd, StackBytesFree); // Now walk the objects and actually assign base offsets to them. diff --git a/llvm/lib/CodeGen/StackProtector.cpp b/llvm/lib/CodeGen/StackProtector.cpp index 9ec1d0630952d..f03518999b10e 100644 --- a/llvm/lib/CodeGen/StackProtector.cpp +++ b/llvm/lib/CodeGen/StackProtector.cpp @@ -55,6 +55,9 @@ using namespace llvm; #define DEBUG_TYPE "stack-protector" STATISTIC(NumFunProtected, "Number of functions protected"); +STATISTIC(NumFunLayoutOnly, + "Number of functions that have their layouts modified, but no " + "protector inserted"); STATISTIC(NumAddrTaken, "Number of local variables that have their address" " taken."); @@ -87,6 +90,7 @@ void SSPLayoutInfo::copyToMachineFrameInfo(MachineFrameInfo &MFI) const { if (Layout.empty()) return; + bool HasLayout = false; for (int I = 0, E = MFI.getObjectIndexEnd(); I != E; ++I) { if (MFI.isDeadObjectIndex(I)) continue; @@ -100,7 +104,23 @@ void SSPLayoutInfo::copyToMachineFrameInfo(MachineFrameInfo &MFI) const { continue; MFI.setObjectSSPLayout(I, LI->second); + HasLayout = true; } + + if (HasLayout && LayoutOnly) + MFI.setStackProtectorLayoutOnly(); +} + +/// Return true if F's stack objects should be laid out as if it had a stack +/// protector, but without inserting one. Functions with funclets are not +/// correctly supported, so are excluded. +static bool isLayoutOnly(const Function &F) { + if (!F.hasFnAttribute("stack-protector-layout-only")) + return false; + if (F.hasPersonalityFn() && + isFuncletEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) + return false; + return true; } SSPLayoutInfo SSPLayoutAnalysis::run(Function &F, @@ -109,6 +129,7 @@ SSPLayoutInfo SSPLayoutAnalysis::run(Function &F, SSPLayoutInfo Info; Info.RequireStackProtector = SSPLayoutAnalysis::requiresStackProtector(&F, &Info.Layout); + Info.LayoutOnly = isLayoutOnly(F); Info.SSPBufferSize = F.getFnAttributeAsParsedInteger( "stack-protector-buffer-size", SSPLayoutInfo::DefaultSSPBufferSize); return Info; @@ -125,6 +146,11 @@ PreservedAnalyses StackProtectorPass::run(Function &F, if (!Info.RequireStackProtector) return PreservedAnalyses::all(); + if (Info.LayoutOnly) { + ++NumFunLayoutOnly; + return PreservedAnalyses::all(); + } + // TODO(etienneb): Functions with funclets are not correctly supported now. // Do nothing if this is funclet-based personality. if (F.hasPersonalityFn()) { @@ -191,6 +217,7 @@ bool StackProtector::runOnFunction(Function &Fn) { if (auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>()) DTU.emplace(DTWP->getDomTree(), DomTreeUpdater::UpdateStrategy::Lazy); TM = &getAnalysis<TargetPassConfig>().getTM<TargetMachine>(); + LayoutInfo.LayoutOnly = isLayoutOnly(Fn); LayoutInfo.HasPrologue = false; LayoutInfo.HasIRCheck = false; @@ -199,6 +226,11 @@ bool StackProtector::runOnFunction(Function &Fn) { if (!requiresStackProtector(F, &LayoutInfo.Layout)) return false; + if (LayoutInfo.LayoutOnly) { + ++NumFunLayoutOnly; + return false; + } + // TODO(etienneb): Functions with funclets are not correctly supported now. // Do nothing if this is funclet-based personality. if (Fn.hasPersonalityFn()) { diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp index acff5e19b06d7..8fb2d08c5c0be 100644 --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2454,7 +2454,8 @@ bool TargetLoweringObjectFileXCOFF::ShouldEmitEHBlock( bool TargetLoweringObjectFileXCOFF::ShouldSetSSPCanaryBitInTB( const MachineFunction *MF) { const Function &F = MF->getFunction(); - if (!F.hasStackProtectorFnAttr()) + if (!F.hasStackProtectorFnAttr() || + F.hasFnAttribute("stack-protector-layout-only")) return false; // FIXME: check presence of canary word // There are cases that the stack protectors are not really inserted even if diff --git a/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll b/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll new file mode 100644 index 0000000000000..1f6c92fd0895d --- /dev/null +++ b/llvm/test/CodeGen/AArch64/stack-protector-layout-only-codegen.ll @@ -0,0 +1,140 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -o - %s | FileCheck %s --check-prefixes=CHECK,OPT +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -O0 -o - %s | FileCheck %s --check-prefixes=CHECK,O0 + +; Check that no stack protector is emitted for functions with +; "stack-protector-layout-only", with and without GlobalISel. + +declare void @use(ptr, ptr, ptr) +declare void @sink(i32) + +define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" { +; CHECK-LABEL: layout_strong: +; CHECK: // %bb.0: +; CHECK-NEXT: sub sp, sp, #48 +; CHECK-NEXT: str x30, [sp, #32] // 8-byte Spill +; CHECK-NEXT: .cfi_def_cfa_offset 48 +; CHECK-NEXT: .cfi_offset w30, -16 +; CHECK-NEXT: str w0, [sp, #4] +; CHECK-NEXT: add x0, sp, #16 +; CHECK-NEXT: add x1, sp, #12 +; CHECK-NEXT: add x2, sp, #8 +; CHECK-NEXT: bl use +; CHECK-NEXT: ldr w0, [sp, #4] +; CHECK-NEXT: bl sink +; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Reload +; CHECK-NEXT: add sp, sp, #48 +; CHECK-NEXT: ret + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" { +; CHECK-LABEL: layout_ssp: +; CHECK: // %bb.0: +; CHECK-NEXT: sub sp, sp, #48 +; CHECK-NEXT: str x30, [sp, #32] // 8-byte Spill +; CHECK-NEXT: .cfi_def_cfa_offset 48 +; CHECK-NEXT: .cfi_offset w30, -16 +; CHECK-NEXT: str w0, [sp, #12] +; CHECK-NEXT: add x0, sp, #16 +; CHECK-NEXT: add x1, sp, #4 +; CHECK-NEXT: add x2, sp, #8 +; CHECK-NEXT: bl use +; CHECK-NEXT: ldr w0, [sp, #12] +; CHECK-NEXT: bl sink +; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Reload +; CHECK-NEXT: add sp, sp, #48 +; CHECK-NEXT: ret + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +define void @req_empty() sspreq "stack-protector-layout-only" { +; CHECK-LABEL: req_empty: +; CHECK: // %bb.0: +; CHECK-NEXT: ret + ret void +} + +define void @cookie(i32 %n) sspstrong { +; OPT-LABEL: cookie: +; OPT: // %bb.0: +; OPT-NEXT: sub sp, sp, #64 +; OPT-NEXT: str x30, [sp, #48] // 8-byte Spill +; OPT-NEXT: .cfi_def_cfa_offset 64 +; OPT-NEXT: .cfi_offset w30, -16 +; OPT-NEXT: adrp x8, __stack_chk_guard +; OPT-NEXT: add x1, sp, #12 +; OPT-NEXT: add x2, sp, #8 +; OPT-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard] +; OPT-NEXT: str x8, [sp, #40] +; OPT-NEXT: str w0, [sp, #4] +; OPT-NEXT: add x0, sp, #16 +; OPT-NEXT: bl use +; OPT-NEXT: ldr w0, [sp, #4] +; OPT-NEXT: bl sink +; OPT-NEXT: adrp x8, __stack_chk_guard +; OPT-NEXT: ldr x9, [sp, #40] +; OPT-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard] +; OPT-NEXT: cmp x8, x9 +; OPT-NEXT: b.ne .LBB3_2 +; OPT-NEXT: // %bb.1: +; OPT-NEXT: ldr x30, [sp, #48] // 8-byte Reload +; OPT-NEXT: add sp, sp, #64 +; OPT-NEXT: ret +; OPT-NEXT: .LBB3_2: +; OPT-NEXT: bl __stack_chk_fail +; +; O0-LABEL: cookie: +; O0: // %bb.0: +; O0-NEXT: sub sp, sp, #64 +; O0-NEXT: str x30, [sp, #48] // 8-byte Spill +; O0-NEXT: .cfi_def_cfa_offset 64 +; O0-NEXT: .cfi_offset w30, -16 +; O0-NEXT: adrp x8, __stack_chk_guard +; O0-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard] +; O0-NEXT: str x8, [sp, #40] +; O0-NEXT: str w0, [sp, #4] +; O0-NEXT: add x0, sp, #16 +; O0-NEXT: add x1, sp, #12 +; O0-NEXT: add x2, sp, #8 +; O0-NEXT: bl use +; O0-NEXT: ldr w0, [sp, #4] +; O0-NEXT: bl sink +; O0-NEXT: ldr x9, [sp, #40] +; O0-NEXT: adrp x8, __stack_chk_guard +; O0-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard] +; O0-NEXT: subs x8, x8, x9 +; O0-NEXT: b.ne .LBB3_1 +; O0-NEXT: b .LBB3_2 +; O0-NEXT: .LBB3_1: +; O0-NEXT: bl __stack_chk_fail +; O0-NEXT: .LBB3_2: +; O0-NEXT: ldr x30, [sp, #48] // 8-byte Reload +; O0-NEXT: add sp, sp, #64 +; O0-NEXT: ret + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} diff --git a/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll b/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll new file mode 100644 index 0000000000000..533c03d06aeeb --- /dev/null +++ b/llvm/test/CodeGen/AArch64/stack-protector-layout-only.ll @@ -0,0 +1,93 @@ +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -stop-after=prolog-epilog -o - %s | FileCheck %s +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -stack-symbol-ordering=0 -O0 -stop-after=prolog-epilog -o - %s | FileCheck %s + +; Check that with "stack-protector-layout-only" the stack objects are ordered +; as if there were a stack protector, but one is not inserted. + +declare void @use(ptr, ptr, ptr) +declare void @sink(i32) + +; CHECK-LABEL: name: layout_strong +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: true +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]], +; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR+4]], +; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR+8]], +; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+12]], +define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +; CHECK-LABEL: name: layout_ssp +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: true +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]], +; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]], +; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]], +; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+4]], +define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +; CHECK-LABEL: name: req_empty +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: false +define void @req_empty() sspreq "stack-protector-layout-only" { + ret void +} + +; Control: without a stack protector attribute the objects keep their order; at +; -O2 the small ones may be scavenged into callee-save holes. +; CHECK-LABEL: name: control +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: false +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]], +; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]], +; CHECK: - { id: 2, name: small, type: default, offset: -{{[0-9]+}}, +; CHECK: - { id: 3, name: large, type: default, offset: -{{[0-9]+}}, +define void @control(i32 %n) { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +; Control: without the attribute a stack protector is still inserted. +; CHECK-LABEL: name: cookie +; CHECK: stackProtector: '%stack.{{[0-9]+}}.StackGuardSlot' +; CHECK-NEXT: stackProtectorLayoutOnly: false +define void @cookie(i32 %n) sspstrong { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} diff --git a/llvm/test/CodeGen/MIR/Generic/frame-info.mir b/llvm/test/CodeGen/MIR/Generic/frame-info.mir index 9879559e5e85a..175bc56453154 100644 --- a/llvm/test/CodeGen/MIR/Generic/frame-info.mir +++ b/llvm/test/CodeGen/MIR/Generic/frame-info.mir @@ -58,6 +58,7 @@ tracksRegLiveness: true # CHECK-NEXT: hasCalls: false # CHECK-NEXT: framePointerPolicy: none # CHECK-NEXT: stackProtector: '' +# CHECK-NEXT: stackProtectorLayoutOnly: false # CHECK-NEXT: functionContext: '' # CHECK-NEXT: maxCallFrameSize: # CHECK-NEXT: cvBytesOfCalleeSavedRegisters: 0 @@ -93,6 +94,7 @@ stack: # CHECK-NEXT: hasCalls: true # CHECK-NEXT: framePointerPolicy: none # CHECK-NEXT: stackProtector: '' +# CHECK-NEXT: stackProtectorLayoutOnly: true # CHECK-NEXT: functionContext: '%stack.0' # CHECK-NEXT: maxCallFrameSize: 4 # CHECK-NEXT: cvBytesOfCalleeSavedRegisters: 8 @@ -111,6 +113,7 @@ frameInfo: maxAlignment: 4 adjustsStack: true hasCalls: true + stackProtectorLayoutOnly: true functionContext: '%stack.0' maxCallFrameSize: 4 cvBytesOfCalleeSavedRegisters: 8 diff --git a/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll b/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll new file mode 100644 index 0000000000000..a8253555d6522 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/stack-protector-layout-only-codegen.ll @@ -0,0 +1,120 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 +; RUN: llc -mtriple=riscv64 -o - %s | FileCheck %s + +; Check that no stack protector is emitted for functions with +; "stack-protector-layout-only". + +declare void @use(ptr, ptr, ptr) +declare void @sink(i32) + +define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" { +; CHECK-LABEL: layout_strong: +; CHECK: # %bb.0: +; CHECK-NEXT: addi sp, sp, -48 +; CHECK-NEXT: .cfi_def_cfa_offset 48 +; CHECK-NEXT: sd ra, 40(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: sw a0, 4(sp) +; CHECK-NEXT: addi a0, sp, 16 +; CHECK-NEXT: addi a1, sp, 12 +; CHECK-NEXT: addi a2, sp, 8 +; CHECK-NEXT: call use +; CHECK-NEXT: lw a0, 4(sp) +; CHECK-NEXT: call sink +; CHECK-NEXT: ld ra, 40(sp) # 8-byte Folded Reload +; CHECK-NEXT: .cfi_restore ra +; CHECK-NEXT: addi sp, sp, 48 +; CHECK-NEXT: .cfi_def_cfa_offset 0 +; CHECK-NEXT: ret + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" { +; CHECK-LABEL: layout_ssp: +; CHECK: # %bb.0: +; CHECK-NEXT: addi sp, sp, -48 +; CHECK-NEXT: .cfi_def_cfa_offset 48 +; CHECK-NEXT: sd ra, 40(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: sw a0, 12(sp) +; CHECK-NEXT: addi a0, sp, 16 +; CHECK-NEXT: addi a1, sp, 4 +; CHECK-NEXT: addi a2, sp, 8 +; CHECK-NEXT: call use +; CHECK-NEXT: lw a0, 12(sp) +; CHECK-NEXT: call sink +; CHECK-NEXT: ld ra, 40(sp) # 8-byte Folded Reload +; CHECK-NEXT: .cfi_restore ra +; CHECK-NEXT: addi sp, sp, 48 +; CHECK-NEXT: .cfi_def_cfa_offset 0 +; CHECK-NEXT: ret + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +define void @req_empty() sspreq "stack-protector-layout-only" { +; CHECK-LABEL: req_empty: +; CHECK: # %bb.0: +; CHECK-NEXT: ret + ret void +} + +define void @cookie(i32 %n) sspstrong { +; CHECK-LABEL: cookie: +; CHECK: # %bb.0: +; CHECK-NEXT: addi sp, sp, -64 +; CHECK-NEXT: .cfi_def_cfa_offset 64 +; CHECK-NEXT: sd ra, 56(sp) # 8-byte Folded Spill +; CHECK-NEXT: sd s0, 48(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: .cfi_offset s0, -16 +; CHECK-NEXT: .cfi_remember_state +; CHECK-NEXT: lui s0, %hi(__stack_chk_guard) +; CHECK-NEXT: ld a1, %lo(__stack_chk_guard)(s0) +; CHECK-NEXT: sd a1, 40(sp) +; CHECK-NEXT: sw a0, 4(sp) +; CHECK-NEXT: addi a0, sp, 16 +; CHECK-NEXT: addi a1, sp, 12 +; CHECK-NEXT: addi a2, sp, 8 +; CHECK-NEXT: call use +; CHECK-NEXT: lw a0, 4(sp) +; CHECK-NEXT: call sink +; CHECK-NEXT: ld a0, %lo(__stack_chk_guard)(s0) +; CHECK-NEXT: ld a1, 40(sp) +; CHECK-NEXT: bne a0, a1, .LBB3_2 +; CHECK-NEXT: # %bb.1: +; CHECK-NEXT: ld ra, 56(sp) # 8-byte Folded Reload +; CHECK-NEXT: ld s0, 48(sp) # 8-byte Folded Reload +; CHECK-NEXT: .cfi_restore ra +; CHECK-NEXT: .cfi_restore s0 +; CHECK-NEXT: addi sp, sp, 64 +; CHECK-NEXT: .cfi_def_cfa_offset 0 +; CHECK-NEXT: ret +; CHECK-NEXT: .LBB3_2: +; CHECK-NEXT: .cfi_restore_state +; CHECK-NEXT: call __stack_chk_fail + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} diff --git a/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll b/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll new file mode 100644 index 0000000000000..41af3da7ed3a4 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/stack-protector-layout-only.ll @@ -0,0 +1,94 @@ +; RUN: llc -mtriple=riscv64 -stop-after=prolog-epilog -o - %s | FileCheck %s +; RUN: llc -mtriple=riscv64 -O0 -global-isel -stop-after=prolog-epilog -o - %s | FileCheck %s + +; Check that with "stack-protector-layout-only" the stack objects are ordered +; as if there were a stack protector, but one is not inserted. + +declare void @use(ptr, ptr, ptr) +declare void @sink(i32) + +; CHECK-LABEL: name: layout_strong +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: true +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]], +; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR+4]], +; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR+8]], +; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+12]], +define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +; With ssp only the large array moves; the rest keep their order. +; CHECK-LABEL: name: layout_ssp +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: true +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]], +; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]], +; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]], +; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+4]], +define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +; Nothing to lay out, so stackProtectorLayoutOnly stays false. +; CHECK-LABEL: name: req_empty +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: false +define void @req_empty() sspreq "stack-protector-layout-only" { + ret void +} + +; Control: without a stack protector attribute the objects keep their order. +; CHECK-LABEL: name: control +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: false +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]], +; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]], +; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]], +; CHECK: - { id: 3, name: large, type: default, offset: -{{[0-9]+}}, +define void @control(i32 %n) { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +; Control: without the attribute a stack protector is still inserted. +; CHECK-LABEL: name: cookie +; CHECK: stackProtector: '%stack.{{[0-9]+}}.StackGuardSlot' +; CHECK-NEXT: stackProtectorLayoutOnly: false +define void @cookie(i32 %n) sspstrong { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} diff --git a/llvm/test/CodeGen/X86/stack-protector-layout-only-funclet.ll b/llvm/test/CodeGen/X86/stack-protector-layout-only-funclet.ll new file mode 100644 index 0000000000000..f96c909ce4a4d --- /dev/null +++ b/llvm/test/CodeGen/X86/stack-protector-layout-only-funclet.ll @@ -0,0 +1,37 @@ +; RUN: llc -mtriple=x86_64-pc-windows-msvc -stop-after=prolog-epilog -o - %s | FileCheck %s + +; Check that "stack-protector-layout-only" is ignored for functions with +; funclets, which StackProtector does not support. + +declare void @may_throw() +declare void @use(ptr) +declare i32 @__CxxFrameHandler3(...) + +; CHECK-LABEL: name: funclet +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: false +define void @funclet() sspstrong "stack-protector-layout-only" personality ptr @__CxxFrameHandler3 { +entry: + %buf = alloca [16 x i8], align 1 + call void @use(ptr %buf) + invoke void @may_throw() + to label %cont unwind label %lpad +cont: + ret void +lpad: + %cs = catchswitch within none [label %catch] unwind to caller +catch: + %p = catchpad within %cs [ptr null, i32 64, ptr null] + catchret from %p to label %cont +} + +; Control: the same function without funclets. +; CHECK-LABEL: name: no_funclet +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: true +define void @no_funclet() sspstrong "stack-protector-layout-only" { +entry: + %buf = alloca [16 x i8], align 1 + call void @use(ptr %buf) + ret void +} diff --git a/llvm/test/CodeGen/X86/stack-protector-layout-only.ll b/llvm/test/CodeGen/X86/stack-protector-layout-only.ll new file mode 100644 index 0000000000000..809481bfa7d62 --- /dev/null +++ b/llvm/test/CodeGen/X86/stack-protector-layout-only.ll @@ -0,0 +1,94 @@ +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -stack-symbol-ordering=0 -stop-after=prolog-epilog -o - %s | FileCheck %s +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -stack-symbol-ordering=0 -O0 -stop-after=prolog-epilog -o - %s | FileCheck %s + +; Check that with "stack-protector-layout-only" the stack objects are ordered +; as if there were a stack protector, but one is not inserted. + +declare void @use(ptr, ptr, ptr) +declare void @sink(i32) + +; CHECK-LABEL: name: layout_strong +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: true +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]], +; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR+4]], +; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR+8]], +; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+12]], +define void @layout_strong(i32 %n) sspstrong "stack-protector-layout-only" { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +; With ssp only the large array moves; the rest keep their order. +; CHECK-LABEL: name: layout_ssp +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: true +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]], +; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]], +; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]], +; CHECK: - { id: 3, name: large, type: default, offset: [[#SCALAR+4]], +define void @layout_ssp(i32 %n) ssp "stack-protector-layout-only" { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +; Nothing to lay out, so stackProtectorLayoutOnly stays false. +; CHECK-LABEL: name: req_empty +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: false +define void @req_empty() sspreq "stack-protector-layout-only" { + ret void +} + +; Control: without a stack protector attribute the objects keep their order. +; CHECK-LABEL: name: control +; CHECK: stackProtector: '' +; CHECK-NEXT: stackProtectorLayoutOnly: false +; CHECK: stack: +; CHECK-NEXT: - { id: 0, name: scalar, type: default, offset: [[#%d,SCALAR:]], +; CHECK: - { id: 1, name: addrof, type: default, offset: [[#SCALAR-4]], +; CHECK: - { id: 2, name: small, type: default, offset: [[#SCALAR-8]], +; CHECK: - { id: 3, name: large, type: default, offset: -{{[0-9]+}}, +define void @control(i32 %n) { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} + +; Control: without the attribute a stack protector is still inserted. +; CHECK-LABEL: name: cookie +; CHECK: stackProtector: '%stack.{{[0-9]+}}.StackGuardSlot' +; CHECK-NEXT: stackProtectorLayoutOnly: false +define void @cookie(i32 %n) sspstrong { + %scalar = alloca i32, align 4 + %addrof = alloca i32, align 4 + %small = alloca [4 x i8], align 4 + %large = alloca [16 x i8], align 16 + store i32 %n, ptr %scalar + call void @use(ptr %large, ptr %small, ptr %addrof) + %v = load i32, ptr %scalar + call void @sink(i32 %v) + ret void +} diff --git a/llvm/test/Transforms/Inline/inline_ssp_layout_only.ll b/llvm/test/Transforms/Inline/inline_ssp_layout_only.ll new file mode 100644 index 0000000000000..f9269463b0679 --- /dev/null +++ b/llvm/test/Transforms/Inline/inline_ssp_layout_only.ll @@ -0,0 +1,86 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6 +; RUN: opt -passes=inline -S -o - %s | FileCheck %s +; Ensure "stack-protector-layout-only" is neither added to nor removed from the +; caller when inlining, while the SSP attributes are still merged. + +declare void @sink() + +define internal void @callee_nossp() alwaysinline { + call void @sink() + ret void +} + +define internal void @callee_sspstrong() sspstrong alwaysinline { + call void @sink() + ret void +} + +define internal void @callee_ssp_layout_only() ssp "stack-protector-layout-only" alwaysinline { + call void @sink() + ret void +} + +define internal void @callee_sspreq_layout_only() sspreq "stack-protector-layout-only" alwaysinline { + call void @sink() + ret void +} + +define internal void @callee_sspstrong_layout_only() sspstrong "stack-protector-layout-only" alwaysinline { + call void @sink() + ret void +} + +define void @caller_ssp_layout_only_callee_sspstrong() ssp "stack-protector-layout-only" { +; CHECK-LABEL: define void @caller_ssp_layout_only_callee_sspstrong( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: call void @sink() +; CHECK-NEXT: ret void +; + call void @callee_sspstrong() + ret void +} + +define void @caller_sspstrong_callee_ssp_layout_only() sspstrong { +; CHECK-LABEL: define void @caller_sspstrong_callee_ssp_layout_only( +; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: call void @sink() +; CHECK-NEXT: ret void +; + call void @callee_ssp_layout_only() + ret void +} + +define void @caller_ssp_callee_sspreq_layout_only() ssp { +; CHECK-LABEL: define void @caller_ssp_callee_sspreq_layout_only( +; CHECK-SAME: ) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: call void @sink() +; CHECK-NEXT: ret void +; + call void @callee_sspreq_layout_only() + ret void +} + +define void @caller_nossp_callee_sspstrong_layout_only() { +; CHECK-LABEL: define void @caller_nossp_callee_sspstrong_layout_only() { +; CHECK-NEXT: call void @sink() +; CHECK-NEXT: ret void +; + call void @callee_sspstrong_layout_only() + ret void +} + +define void @caller_sspreq_layout_only_callee_nossp() sspreq "stack-protector-layout-only" { +; CHECK-LABEL: define void @caller_sspreq_layout_only_callee_nossp( +; CHECK-SAME: ) #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: call void @sink() +; CHECK-NEXT: ret void +; + call void @callee_nossp() + ret void +} +;. +; CHECK: attributes #[[ATTR0]] = { sspstrong "stack-protector-layout-only" } +; CHECK: attributes #[[ATTR1]] = { sspstrong } +; CHECK: attributes #[[ATTR2]] = { sspreq } +; CHECK: attributes #[[ATTR3]] = { sspreq "stack-protector-layout-only" } +;. diff --git a/llvm/test/Transforms/StackProtector/layout-only-no-analysis.ll b/llvm/test/Transforms/StackProtector/layout-only-no-analysis.ll new file mode 100644 index 0000000000000..c63f2084c7efe --- /dev/null +++ b/llvm/test/Transforms/StackProtector/layout-only-no-analysis.ll @@ -0,0 +1,14 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +;; StackProtector inserts no __stack_chk_fail call for functions with +;; "stack-protector-layout-only", so it must not require +;; LibcallLoweringModuleAnalysis for them. +; REQUIRES: x86-registered-target +; RUN: opt -mtriple=x86_64-- -passes=stack-protector -S -o - %s | FileCheck %s + +define void @empty() sspreq "stack-protector-layout-only" { +; CHECK-LABEL: define void @empty( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: ret void +; + ret void +} diff --git a/llvm/test/Transforms/StackProtector/layout-only-no-protector.ll b/llvm/test/Transforms/StackProtector/layout-only-no-protector.ll new file mode 100644 index 0000000000000..eb82ab9f8798f --- /dev/null +++ b/llvm/test/Transforms/StackProtector/layout-only-no-protector.ll @@ -0,0 +1,54 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +;; Check that no stack protector is inserted for functions with +;; "stack-protector-layout-only", whether one is required by a buffer +;; (sspstrong) or unconditionally (sspreq). +; REQUIRES: x86-registered-target +; RUN: opt -mtriple=x86_64-pc-linux-gnu -passes='require<libcall-lowering-info>,stack-protector' -S -o - %s | FileCheck %s + +declare void @use(ptr) + +define void @layout_only_strong() sspstrong "stack-protector-layout-only" { +; CHECK-LABEL: define void @layout_only_strong( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[BUF:%.*]] = alloca [16 x i8], align 1 +; CHECK-NEXT: call void @use(ptr [[BUF]]) +; CHECK-NEXT: ret void +; + %buf = alloca [16 x i8], align 1 + call void @use(ptr %buf) + ret void +} + +define void @layout_only_req() sspreq "stack-protector-layout-only" { +; CHECK-LABEL: define void @layout_only_req( +; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: ret void +; + ret void +} + +define void @cookie() sspstrong { +; CHECK-LABEL: define void @cookie( +; CHECK-SAME: ) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)), align 8 +; CHECK-NEXT: call void @llvm.stackprotector(ptr [[STACKGUARD]], ptr [[STACKGUARDSLOT]]) +; CHECK-NEXT: [[BUF:%.*]] = alloca [16 x i8], align 1 +; CHECK-NEXT: call void @use(ptr [[BUF]]) +; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)), align 8 +; CHECK-NEXT: [[TMP1:%.*]] = load volatile ptr, ptr [[STACKGUARDSLOT]], align 8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[STACKGUARD1]], [[TMP1]] +; CHECK-NEXT: br i1 [[TMP2]], label %[[SP_RETURN:.*]], label %[[CALLSTACKCHECKFAILBLK:.*]], !prof [[PROF0:![0-9]+]] +; CHECK: [[SP_RETURN]]: +; CHECK-NEXT: ret void +; CHECK: [[CALLSTACKCHECKFAILBLK]]: +; CHECK-NEXT: call void @__stack_chk_fail() #[[ATTR4:[0-9]+]] +; CHECK-NEXT: unreachable +; + %buf = alloca [16 x i8], align 1 + call void @use(ptr %buf) + ret void +} +;. +; CHECK: [[PROF0]] = !{!"branch_weights", i32 2147481600, i32 2048} +;. diff --git a/llvm/tools/llvm-reduce/ReducerWorkItem.cpp b/llvm/tools/llvm-reduce/ReducerWorkItem.cpp index 6b202aec3e8f8..ec960361125d7 100644 --- a/llvm/tools/llvm-reduce/ReducerWorkItem.cpp +++ b/llvm/tools/llvm-reduce/ReducerWorkItem.cpp @@ -170,6 +170,8 @@ static void cloneFrameInfo( DstMFI.setStackProtectorIndex(SrcMFI.getStackProtectorIndex()); } + DstMFI.setStackProtectorLayoutOnly(SrcMFI.hasStackProtectorLayoutOnly()); + // FIXME: Needs test, missing MIR serialization. if (SrcMFI.hasFunctionContextIndex()) { DstMFI.setFunctionContextIndex(SrcMFI.getFunctionContextIndex()); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
