Index: clang/include/clang/Basic/CodeGenOptions.h =================================================================== --- clang/include/clang/Basic/CodeGenOptions.h +++ clang/include/clang/Basic/CodeGenOptions.h @@ -327,6 +327,10 @@ /// by sanitizer coverage pass. std::vector SanitizeCoverageAllowlistFiles; + std::string StackProtectorGuard; + + std::string StackProtectorGuardReg; + /// Path to blocklist file specifying which objects /// (files, functions) listed for instrumentation by sanitizer /// coverage pass should actually not be instrumented. Index: clang/include/clang/Basic/CodeGenOptions.def =================================================================== --- clang/include/clang/Basic/CodeGenOptions.def +++ clang/include/clang/Basic/CodeGenOptions.def @@ -356,6 +356,9 @@ /// Bit size of immediate TLS offsets (0 == use the default). VALUE_CODEGENOPT(TLSSize, 8, 0) +/// The default stack protector guard offset to use. +VALUE_CODEGENOPT(StackProtectorGuardOffset, 32, 40) + /// Number of path components to strip when emitting checks. (0 == full /// filename) VALUE_CODEGENOPT(EmitCheckPathComponentsToStrip, 32, 0) Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -2242,6 +2242,8 @@ def march_EQ : Joined<["-"], "march=">, Group, Flags<[CoreOption]>; def masm_EQ : Joined<["-"], "masm=">, Group, Flags<[DriverOption]>; def mcmodel_EQ : Joined<["-"], "mcmodel=">, Group, Flags<[CC1Option]>; +def mstack_protector_guard_EQ : Joined<["-"], "mstack-protector-guard=">, Group, Flags<[DriverOption, CC1Option]>, + HelpText<"Use the given guard (global, tls) for addressing the stack-protector guard.">; def mtls_size_EQ : Joined<["-"], "mtls-size=">, Group, Flags<[DriverOption, CC1Option]>, HelpText<"Specify bit size of immediate TLS offsets (AArch64 ELF only): " "12 (for 4KB) | 24 (for 16MB, default) | 32 (for 4GB) | 48 (for 256TB, needs -mcmodel=large)">; @@ -2596,6 +2598,10 @@ def mrecip_EQ : CommaJoined<["-"], "mrecip=">, Group, Flags<[CC1Option]>; def mprefer_vector_width_EQ : Joined<["-"], "mprefer-vector-width=">, Group, Flags<[CC1Option]>, HelpText<"Specifies preferred vector width for auto-vectorization. Defaults to 'none' which allows target specific decisions.">; +def mstack_protector_guard_offset_EQ : Joined<["-"], "mstack-protector-guard-offset=">, Group, Flags<[CC1Option]>, + HelpText<"Use the given offset for addressing the stack-protector guard.">; +def mstack_protector_guard_reg_EQ : Joined<["-"], "mstack-protector-guard-reg=">, Group, Flags<[DriverOption, CC1Option]>, + HelpText<"Use the given reg for addressing the stack-protector guard.">; def mpie_copy_relocations : Flag<["-"], "mpie-copy-relocations">, Group, Flags<[CC1Option]>, HelpText<"Use copy relocations support for PIE builds">; Index: clang/lib/CodeGen/BackendUtil.cpp =================================================================== --- clang/lib/CodeGen/BackendUtil.cpp +++ clang/lib/CodeGen/BackendUtil.cpp @@ -520,6 +520,20 @@ Options.UniqueSectionNames = CodeGenOpts.UniqueSectionNames; Options.UniqueBasicBlockSectionNames = CodeGenOpts.UniqueBasicBlockSectionNames; + Options.StackProtectorGuard = + llvm::StringSwitch(CodeGenOpts + .StackProtectorGuard) + .Case("tls", llvm::StackProtectorGuards::TLS) + .Case("global", llvm::StackProtectorGuards::Global) + .Default(llvm::StackProtectorGuards::None); + Options.StackProtectorGuardOffset = CodeGenOpts.StackProtectorGuardOffset; + Options.StackProtectorGuardReg = + llvm::StringSwitch(CodeGenOpts + .StackProtectorGuardReg) + .Case("fs", llvm::StackProtectorGuardRegs::FS) + .Case("gs", llvm::StackProtectorGuardRegs::GS) + .Case("ss", llvm::StackProtectorGuardRegs::SS) + .Default(llvm::StackProtectorGuardRegs::None); Options.TLSSize = CodeGenOpts.TLSSize; Options.EmulatedTLS = CodeGenOpts.EmulatedTLS; Options.ExplicitEmulatedTLS = CodeGenOpts.ExplicitEmulatedTLS; Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -2955,8 +2955,9 @@ Args.AddAllArgValues(CmdArgs, options::OPT_Xanalyzer); } -static void RenderSSPOptions(const ToolChain &TC, const ArgList &Args, - ArgStringList &CmdArgs, bool KernelOrKext) { +static void RenderSSPOptions(const Driver &D, const ToolChain &TC, + const ArgList &Args, ArgStringList &CmdArgs, + bool KernelOrKext) { const llvm::Triple &EffectiveTriple = TC.getEffectiveTriple(); // NVPTX doesn't support stack protectors; from the compiler's perspective, it @@ -3001,6 +3002,33 @@ A->claim(); } } + + Arg * A = Args.getLastArg(options::OPT_mstack_protector_guard_EQ); + if (A) { + StringRef Value = A->getValue(); + if (Value != "tls" && Value != "global") + D.Diag(diag::err_drv_invalid_value) << A->getOption().getName() << Value; + CmdArgs.push_back(Args.MakeArgString("-mstack-protector-guard=" + + Value)); + } + + A = Args.getLastArg(options::OPT_mstack_protector_guard_offset_EQ); + if (A) { + StringRef Value = A->getValue(); + unsigned Offset; + Value.getAsInteger(10, Offset); + CmdArgs.push_back(Args.MakeArgString("-mstack-protector-guard-offset=" + + Value)); + } + + A = Args.getLastArg(options::OPT_mstack_protector_guard_reg_EQ); + if (A) { + StringRef Value = A->getValue(); + if (Value != "fs" && Value != "gs") + D.Diag(diag::err_drv_invalid_value) << A->getOption().getName() << Value; + CmdArgs.push_back(Args.MakeArgString("-mstack-protector-guard-reg=" + + Value)); + } } static void RenderSCPOptions(const ToolChain &TC, const ArgList &Args, @@ -5429,7 +5457,7 @@ options::OPT_mno_speculative_load_hardening, false)) CmdArgs.push_back(Args.MakeArgString("-mspeculative-load-hardening")); - RenderSSPOptions(TC, Args, CmdArgs, KernelOrKext); + RenderSSPOptions(D, TC, Args, CmdArgs, KernelOrKext); RenderSCPOptions(TC, Args, CmdArgs); RenderTrivialAutoVarInitOptions(D, TC, Args, CmdArgs); Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -1260,6 +1260,21 @@ } Opts.SSPBufferSize = getLastArgIntValue(Args, OPT_stack_protector_buffer_size, 8, Diags); + + Opts.StackProtectorGuard = + std::string(Args.getLastArgValue(OPT_mstack_protector_guard_EQ)); + + if (Arg *A = Args.getLastArg(OPT_mstack_protector_guard_offset_EQ)) { + StringRef Val = A->getValue(); + unsigned Offset = Opts.StackProtectorGuardOffset; + Val.getAsInteger(10, Offset); + Opts.StackProtectorGuardOffset = Offset; + } + + Opts.StackProtectorGuardReg = + std::string(Args.getLastArgValue(OPT_mstack_protector_guard_reg_EQ, + "none")); + Opts.StackRealignment = Args.hasArg(OPT_mstackrealign); if (Arg *A = Args.getLastArg(OPT_mstack_alignment)) { StringRef Val = A->getValue(); Index: clang/test/Driver/stack-protector-guard.c =================================================================== --- /dev/null +++ clang/test/Driver/stack-protector-guard.c @@ -0,0 +1,30 @@ +// RUN: %clang -### -target x86_64-linux-gnu -mstack-protector-guard=tls %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-TLS %s +// RUN: %clang -### -target x86_64-linux-gnu -mstack-protector-guard=global %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-GLOBAL %s + +// Invalid option value +// RUN: not %clang -target x86_64-linux-gnu -mstack-protector-guard=zx %s 2>&1 \ +// RUN: | FileCheck -check-prefix=INVALID-VALUE %s + +// CHECK-TLS: "-cc1" {{.*}}"-mstack-protector-guard=tls" +// CHECK-GLOBAL: "-cc1" {{.*}}"-mstack-protector-guard=global" +// INVALID-VALUE: error: invalid value + +// RUN: %clang -### -target x86_64-linux-gnu -mstack-protector-guard-reg=fs %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-FS %s +// RUN: %clang -### -target x86_64-linux-gnu -mstack-protector-guard-reg=gs %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-GS %s + +// Invalid option value +// RUN: not %clang -target x86_64-linux-gnu -mstack-protector-guard-reg=ds %s 2>&1 \ +// RUN: | FileCheck -check-prefix=INVALID-REG %s + +// CHECK-FS: "-cc1" {{.*}}"-mstack-protector-guard-reg=fs" +// CHECK-GS: "-cc1" {{.*}}"-mstack-protector-guard-reg=gs" +// INVALID-REG: error: invalid value + +// RUN: %clang -### -target x86_64-linux-gnu -mstack-protector-guard-offset=30 %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-OFFSET %s + +// CHECK-OFFSET: "-cc1" {{.*}}"-mstack-protector-guard-offset=30" Index: llvm/include/llvm/CodeGen/CommandFlags.h =================================================================== --- llvm/include/llvm/CodeGen/CommandFlags.h +++ llvm/include/llvm/CodeGen/CommandFlags.h @@ -96,6 +96,12 @@ std::string getBBSections(); +std::string getStackProtectorGuard(); + +unsigned getStackProtectorGuardOffset(); + +std::string getStackProtectorGuardReg(); + unsigned getTLSSize(); bool getEmulatedTLS(); @@ -132,6 +138,12 @@ llvm::BasicBlockSection getBBSectionsMode(llvm::TargetOptions &Options); +llvm::StackProtectorGuards +getStackProtectorGuardMode(llvm::TargetOptions &Options); + +llvm::StackProtectorGuardRegs +getStackProtectorGuardRegMode(llvm::TargetOptions &Options); + // Common utility function tightly tied to the options listed here. Initializes // a TargetOptions object with CodeGen flags and returns it. TargetOptions InitTargetOptionsFromCodeGenFlags(); Index: llvm/include/llvm/Target/TargetOptions.h =================================================================== --- llvm/include/llvm/Target/TargetOptions.h +++ llvm/include/llvm/Target/TargetOptions.h @@ -73,6 +73,19 @@ None // Do not use Basic Block Sections. }; + enum class StackProtectorGuards { + TLS, + Global, + None + }; + + enum class StackProtectorGuardRegs { + FS, + GS, + SS, + None + }; + enum class EABI { Unknown, Default, // Default means not specified @@ -242,6 +255,17 @@ /// noreturn calls, even if TrapUnreachable is true. unsigned NoTrapAfterNoreturn : 1; + /// Stack protector guard mode to use: fs or gs. + StackProtectorGuards StackProtectorGuard = + StackProtectorGuards::None; + + /// Stack protector guard offset to use, default value is 40. + unsigned StackProtectorGuardOffset : 32; + + /// Stack protector guard reg to use: fs or gs. + StackProtectorGuardRegs StackProtectorGuardReg = + StackProtectorGuardRegs::None; + /// Bit size of immediate TLS offsets (0 == use the default). unsigned TLSSize : 8; Index: llvm/lib/CodeGen/CommandFlags.cpp =================================================================== --- llvm/lib/CodeGen/CommandFlags.cpp +++ llvm/lib/CodeGen/CommandFlags.cpp @@ -75,6 +75,9 @@ CGOPT_EXP(bool, DataSections) CGOPT_EXP(bool, FunctionSections) CGOPT(std::string, BBSections) +CGOPT(std::string, StackProtectorGuard) +CGOPT(unsigned, StackProtectorGuardOffset) +CGOPT(std::string, StackProtectorGuardReg) CGOPT(unsigned, TLSSize) CGOPT(bool, EmulatedTLS) CGOPT(bool, UniqueSectionNames) @@ -340,6 +343,21 @@ cl::init("none")); CGBINDOPT(BBSections); + static cl::opt StackProtectorGuard( + "stack-protector-guard", cl::desc("Stack protector guard mode"), + cl::init("none")); + CGBINDOPT(StackProtectorGuard); + + static cl::opt StackProtectorGuardReg( + "stack-protector-guard-reg", cl::desc("Stack protector guard register"), + cl::init("none")); + CGBINDOPT(StackProtectorGuardReg); + + static cl::opt StackProtectorGuardOffset( + "stack-protector-guard-offset", cl::desc("Stack protector guard offset"), + cl::init((unsigned)-1)); + CGBINDOPT(StackProtectorGuardOffset); + static cl::opt TLSSize( "tls-size", cl::desc("Bit size of immediate TLS offsets"), cl::init(0)); CGBINDOPT(TLSSize); @@ -451,6 +469,48 @@ } } +llvm::StackProtectorGuardRegs +codegen::getStackProtectorGuardRegMode(llvm::TargetOptions &Options) { + if (getStackProtectorGuardReg() == "fs") + return StackProtectorGuardRegs::FS; + else if (getStackProtectorGuardReg() == "gs") + return StackProtectorGuardRegs::GS; + else if (getStackProtectorGuardReg() == "none") + return StackProtectorGuardRegs::None; + else { + ErrorOr> MBOrErr = + MemoryBuffer::getFile(getStackProtectorGuardReg()); + if (!MBOrErr) { + errs() << "Error illegal stack protector guard register: " + << MBOrErr.getError().message() << "\n"; + } else { + Options.BBSectionsFuncListBuf = std::move(*MBOrErr); + } + return StackProtectorGuardRegs::None; + } +} + +llvm::StackProtectorGuards +codegen::getStackProtectorGuardMode(llvm::TargetOptions &Options) { + if (getStackProtectorGuard() == "tls") + return StackProtectorGuards::TLS; + else if (getStackProtectorGuard() == "global") + return StackProtectorGuards::Global; + else if (getStackProtectorGuard() == "none") + return StackProtectorGuards::None; + else { + ErrorOr> MBOrErr = + MemoryBuffer::getFile(getStackProtectorGuard()); + if (!MBOrErr) { + errs() << "Error illegal stack protector guard mode: " + << MBOrErr.getError().message() << "\n"; + } else { + Options.BBSectionsFuncListBuf = std::move(*MBOrErr); + } + return StackProtectorGuards::None; + } +} + // Common utility function tightly tied to the options listed here. Initializes // a TargetOptions object with CodeGen flags and returns it. TargetOptions codegen::InitTargetOptionsFromCodeGenFlags() { @@ -482,6 +542,9 @@ Options.BBSections = getBBSectionsMode(Options); Options.UniqueSectionNames = getUniqueSectionNames(); Options.UniqueBasicBlockSectionNames = getUniqueBasicBlockSectionNames(); + Options.StackProtectorGuard = getStackProtectorGuardMode(Options); + Options.StackProtectorGuardOffset = getStackProtectorGuardOffset(); + Options.StackProtectorGuardReg = getStackProtectorGuardRegMode(Options); Options.TLSSize = getTLSSize(); Options.EmulatedTLS = getEmulatedTLS(); Options.ExplicitEmulatedTLS = EmulatedTLSView->getNumOccurrences() > 0; Index: llvm/lib/CodeGen/StackProtector.cpp =================================================================== --- llvm/lib/CodeGen/StackProtector.cpp +++ llvm/lib/CodeGen/StackProtector.cpp @@ -381,7 +381,10 @@ static Value *getStackGuard(const TargetLoweringBase *TLI, Module *M, IRBuilder<> &B, bool *SupportsSelectionDAGSP = nullptr) { - if (Value *Guard = TLI->getIRStackGuard(B)) + Value *Guard = TLI->getIRStackGuard(B); + auto GuardMode = TLI->getTargetMachine().Options.StackProtectorGuard; + + if ((GuardMode != llvm::StackProtectorGuards::Global) && Guard) return B.CreateLoad(B.getInt8PtrTy(), Guard, true, "StackGuard"); // Use SelectionDAG SSP handling, since there isn't an IR guard. Index: llvm/lib/Target/X86/X86ISelLowering.cpp =================================================================== --- llvm/lib/Target/X86/X86ISelLowering.cpp +++ llvm/lib/Target/X86/X86ISelLowering.cpp @@ -2467,13 +2467,25 @@ // defines ZX_TLS_STACK_GUARD_OFFSET with this value. return SegmentOffset(IRB, 0x10, getAddressSpace()); } else { + unsigned AddressSpace = getAddressSpace(); + // Specially, some users may customize the base reg and offset. + unsigned Offset = getTargetMachine().Options.StackProtectorGuardOffset; + // If we don't set -stack-protector-guard-offset value: // %fs:0x28, unless we're using a Kernel code model, in which case // it's %gs:0x28. gs:0x14 on i386. - unsigned Offset = (Subtarget.is64Bit()) ? 0x28 : 0x14; - return SegmentOffset(IRB, Offset, getAddressSpace()); + if (Offset == (unsigned)-1) + Offset = (Subtarget.is64Bit()) ? 0x28 : 0x14; + + auto GuardReg = getTargetMachine().Options.StackProtectorGuardReg; + if (GuardReg == StackProtectorGuardRegs::FS) + AddressSpace = X86AS::FS; + else if (GuardReg == StackProtectorGuardRegs::GS) + AddressSpace = X86AS::GS; + else if (GuardReg == StackProtectorGuardRegs::SS) + AddressSpace = X86AS::SS; + return SegmentOffset(IRB, Offset, AddressSpace); } } - return TargetLowering::getIRStackGuard(IRB); } @@ -2495,8 +2507,12 @@ } return; } + + auto GuardMode = getTargetMachine().Options.StackProtectorGuard; + // glibc, bionic, and Fuchsia have a special slot for the stack guard. - if (hasStackGuardSlotTLS(Subtarget.getTargetTriple())) + if ((GuardMode != llvm::StackProtectorGuards::Global) + && hasStackGuardSlotTLS(Subtarget.getTargetTriple())) return; TargetLowering::insertSSPDeclarations(M); } Index: llvm/test/CodeGen/X86/stack-protector-3.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/stack-protector-3.ll @@ -0,0 +1,67 @@ +; RUN: llc -mtriple=x86_64-pc-linux-gnu -o - < %s | FileCheck --check-prefix=CHECK-TLS-FS-40 %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard=tls -o - < %s | FileCheck --check-prefix=CHECK-TLS-FS-40 %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard=global -o - < %s | FileCheck --check-prefix=CHECK-GLOBAL %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard-reg=fs -o - < %s | FileCheck --check-prefix=CHECK-TLS-FS-40 %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard-reg=gs -o - < %s | FileCheck --check-prefix=CHECK-GS %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard-offset=20 -o - < %s | FileCheck --check-prefix=CHECK-OFFSET %s + +; CHECK-TLS-FS-40: movq %fs:40, %rax +; CHECK-TLS-FS-40: movq %fs:40, %rax +; CHECK-TLS-FS-40-NEXT: cmpq 16(%rsp), %rax +; CHECK-TLS-FS-40-NEXT: jne .LBB0_2 +; CHECK-TLS-FS-40: .LBB0_2: +; CHECK-TLS-FS-40-NEXT: .cfi_def_cfa_offset 32 +; CHECK-TLS-FS-40-NEXT: callq __stack_chk_fail + +; CHECK-GS: movq %gs:40, %rax +; CHECK-GS: movq %gs:40, %rax +; CHECK-GS-NEXT: cmpq 16(%rsp), %rax +; CHECK-GS-NEXT: jne .LBB0_2 +; CHECK-GS: .LBB0_2: +; CHECK-GS-NEXT: .cfi_def_cfa_offset 32 +; CHECK-GS-NEXT: callq __stack_chk_fail + +; CHECK-OFFSET: movq %fs:20, %rax +; CHECK-OFFSET: movq %fs:20, %rax +; CHECK-OFFSET-NEXT: cmpq 16(%rsp), %rax +; CHECK-OFFSET-NEXT: jne .LBB0_2 +; CHECK-OFFSET: .LBB0_2: +; CHECK-OFFSET-NEXT: .cfi_def_cfa_offset 32 +; CHECK-OFFSET-NEXT: callq __stack_chk_fail + +; CHECK-GLOBAL: movq __stack_chk_guard(%rip), %rax +; CHECK-GLOBAL: movq __stack_chk_guard(%rip), %rax +; CHECK-GLOBAL-NEXT: cmpq 16(%rsp), %rax +; CHECK-GLOBAL-NEXT: jne .LBB0_2 +; CHECK-GLOBAL: .LBB0_2: +; CHECK-GLOBAL-NEXT: .cfi_def_cfa_offset 32 +; CHECK-GLOBAL-NEXT: callq __stack_chk_fail + +; ModuleID = 't.c' +@.str = private unnamed_addr constant [14 x i8] c"stackoverflow\00", align 1 +@a = dso_local local_unnamed_addr global i8* null, align 8 + +; Function Attrs: nounwind sspreq uwtable writeonly +define dso_local i32 @main() local_unnamed_addr #0 { +entry: + %array = alloca [5 x i8], align 1 + %0 = getelementptr inbounds [5 x i8], [5 x i8]* %array, i64 0, i64 0 + call void @llvm.lifetime.start.p0i8(i64 5, i8* nonnull %0) #2 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(14) %0, i8* nonnull align 1 dereferenceable(14) getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0), i64 14, i1 false) #2 + store i8* %0, i8** @a, align 8 + call void @llvm.lifetime.end.p0i8(i64 5, i8* nonnull %0) #2 + ret i32 0 +} + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +attributes #0 = { nounwind sspreq uwtable writeonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind willreturn } +attributes #2 = { nounwind }