From 2d775ff6583bc57bf6a4cab0cd1e85dcc59bf733 Mon Sep 17 00:00:00 2001 From: Hongjin Li Date: Thu, 4 Aug 2022 10:15:44 +0800 Subject: [PATCH 1/2] feature: retGuard Signed-off-by: Hongjin Li Change-Id: Icf692d81a2e72539c23f9ca8ca0afdf0247c8e28 --- clang/include/clang/Basic/CodeGenOptions.def | 3 + clang/include/clang/Basic/LangOptions.h | 2 +- clang/include/clang/Driver/Options.td | 16 +- clang/lib/CodeGen/CGCall.cpp | 3 + clang/lib/CodeGen/CodeGenModule.cpp | 2 + clang/lib/Driver/ToolChains/Clang.cpp | 46 +- clang/lib/Frontend/CompilerInvocation.cpp | 2 + clang/lib/Frontend/InitPreprocessor.cpp | 2 + .../x86/x86AssemblyInspectionEngine.cpp | 13 + .../x86/x86AssemblyInspectionEngine.h | 1 + llvm/include/llvm/CodeGen/MachineFrameInfo.h | 20 + llvm/include/llvm/CodeGen/Passes.h | 3 + .../llvm/CodeGen/ReturnProtectorLowering.h | 79 ++ llvm/include/llvm/CodeGen/StackProtector.h | 2 + .../llvm/CodeGen/TargetFrameLowering.h | 5 + llvm/include/llvm/IR/Attributes.td | 3 + llvm/include/llvm/InitializePasses.h | 1 + llvm/lib/AsmParser/LLToken.h | 1 + llvm/lib/CodeGen/CMakeLists.txt | 2 + llvm/lib/CodeGen/LocalStackSlotAllocation.cpp | 46 +- llvm/lib/CodeGen/PrologEpilogInserter.cpp | 85 ++- llvm/lib/CodeGen/ReturnProtectorLowering.cpp | 340 +++++++++ llvm/lib/CodeGen/ReturnProtectorPass.cpp | 63 ++ llvm/lib/CodeGen/SafeStack.cpp | 1 + llvm/lib/CodeGen/StackProtector.cpp | 30 + llvm/lib/CodeGen/TargetPassConfig.cpp | 2 + llvm/lib/IR/Attributes.cpp | 10 +- llvm/lib/IR/Function.cpp | 1 + llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 13 + .../Target/AArch64/AArch64FrameLowering.cpp | 9 + .../lib/Target/AArch64/AArch64FrameLowering.h | 8 +- llvm/lib/Target/AArch64/AArch64InstrInfo.td | 8 + .../AArch64ReturnProtectorLowering.cpp | 124 +++ .../AArch64/AArch64ReturnProtectorLowering.h | 52 ++ llvm/lib/Target/AArch64/CMakeLists.txt | 1 + llvm/lib/Target/X86/CMakeLists.txt | 2 + llvm/lib/Target/X86/X86.h | 4 + llvm/lib/Target/X86/X86FixupGadgets.cpp | 708 ++++++++++++++++++ llvm/lib/Target/X86/X86FrameLowering.cpp | 7 +- llvm/lib/Target/X86/X86FrameLowering.h | 5 + llvm/lib/Target/X86/X86InstrCompiler.td | 19 + llvm/lib/Target/X86/X86MCInstLower.cpp | 44 ++ .../Target/X86/X86ReturnProtectorLowering.cpp | 121 +++ .../Target/X86/X86ReturnProtectorLowering.h | 45 ++ llvm/lib/Target/X86/X86TargetMachine.cpp | 1 + llvm/lib/Transforms/Utils/CodeExtractor.cpp | 1 + 46 files changed, 1926 insertions(+), 30 deletions(-) create mode 100644 llvm/include/llvm/CodeGen/ReturnProtectorLowering.h create mode 100644 llvm/lib/CodeGen/ReturnProtectorLowering.cpp create mode 100644 llvm/lib/CodeGen/ReturnProtectorPass.cpp create mode 100644 llvm/lib/Target/AArch64/AArch64ReturnProtectorLowering.cpp create mode 100644 llvm/lib/Target/AArch64/AArch64ReturnProtectorLowering.h create mode 100644 llvm/lib/Target/X86/X86FixupGadgets.cpp create mode 100644 llvm/lib/Target/X86/X86ReturnProtectorLowering.cpp create mode 100644 llvm/lib/Target/X86/X86ReturnProtectorLowering.h diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 9d53b5b923bb..b3acae03ad11 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -325,6 +325,9 @@ VALUE_CODEGENOPT(SmallDataLimit, 32, 0) /// The lower bound for a buffer to be considered for stack protection. VALUE_CODEGENOPT(SSPBufferSize, 32, 0) +/// Whether to use return protectors +CODEGENOPT(ReturnProtector, 1, 0) + /// The kind of generated debug info. ENUM_CODEGENOPT(DebugInfo, codegenoptions::DebugInfoKind, 4, codegenoptions::NoDebugInfo) diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index d8bd2a8b52fc..75ce1f4479f0 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -61,7 +61,7 @@ public: using RoundingMode = llvm::RoundingMode; enum GCMode { NonGC, GCOnly, HybridGC }; - enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq }; + enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq, SSPGuard }; // 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/Driver/Options.td b/clang/include/clang/Driver/Options.td index 9019ea8f7298..0858f84e3d7b 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2258,6 +2258,8 @@ defm signed_char : OptOutFFlag<"signed-char", "char is signed", "char is unsigne def fsplit_stack : Flag<["-"], "fsplit-stack">, Group; def fstack_protector_all : Flag<["-"], "fstack-protector-all">, Group, HelpText<"Enable stack protectors for all functions">; +def fstack_protector_guard : Flag<["-"], "fstack-protector-guard">, Group, + HelpText<"Enable stack protectors with ret-guard">; defm stack_clash_protection : BoolFOption<"stack-clash-protection", CodeGenOpts<"StackClashProtector">, DefaultFalse, PosFlag, NegFlag, @@ -2278,6 +2280,16 @@ def fstack_protector : Flag<["-"], "fstack-protector">, Group, "stack frame such that a buffer overflow from a vulnerable variable will " "overwrite the guard value before overwriting the function's return " "address. The reference stack guard value is stored in a global variable.">; +def ret_protector : Flag<["-"], "ret-protector">, Flags<[CC1Option]>, + HelpText<"Enable Return Protectors">; +def fno_ret_protector : Flag<["-"], "fno-ret-protector">, Group, Flags<[CoreOption]>, + HelpText<"Disable return protector">; +def fret_protector : Flag<["-"], "fret-protector">, Group, Flags<[CoreOption]>, + HelpText<"Enable return protector">; +def fno_fixup_gadgets : Flag<["-"], "fno-fixup-gadgets">, Group, Flags<[CoreOption]>, + HelpText<"Disable FixupGadgets pass (x86 only)">; +def ffixup_gadgets : Flag<["-"], "ffixup-gadgets">, Group, Flags<[CoreOption]>, + HelpText<"Replace ROP friendly instructions with safe alternatives (x86 only)">; def ftrivial_auto_var_init : Joined<["-"], "ftrivial-auto-var-init=">, Group, Flags<[CC1Option, CoreOption]>, HelpText<"Initialize trivial automatic stack variables: uninitialized (default)" " | pattern">, Values<"uninitialized,zero,pattern">, @@ -5115,9 +5127,9 @@ def static_define : Flag<["-"], "static-define">, HelpText<"Should __STATIC__ be defined">, MarshallingInfoFlag>; def stack_protector : Separate<["-"], "stack-protector">, - HelpText<"Enable stack protectors">, Values<"0,1,2,3">, + HelpText<"Enable stack protectors">, Values<"0,1,2,3,4">, NormalizedValuesScope<"LangOptions">, - NormalizedValues<["SSPOff", "SSPOn", "SSPStrong", "SSPReq"]>, + NormalizedValues<["SSPOff", "SSPOn", "SSPStrong", "SSPReq", "SSPGuard"]>, MarshallingInfoString, "SSPOff">, AutoNormalizeEnum; 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/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index bc7582c67989..9bcdbaf64b96 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2123,6 +2123,9 @@ void CodeGenModule::ConstructAttributeList( // CPU/feature overrides. addDefaultFunctionDefinitionAttributes // handles these separately to set them based on the global defaults. GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs); + + if (CodeGenOpts.ReturnProtector) + FuncAttrs.addAttribute("ret-protector"); } // Collect attributes from arguments and return values. diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 9c9bd4e374af..c14300ef63aa 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1677,6 +1677,8 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, B.addAttribute(llvm::Attribute::StackProtectStrong); else if (LangOpts.getStackProtector() == LangOptions::SSPReq) B.addAttribute(llvm::Attribute::StackProtectReq); + else if (LangOpts.getStackProtector() == LangOptions::SSPGuard) + B.addAttribute(llvm::Attribute::StackProtectGuard); } if (!D) { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 32397abf1b54..6601705f518c 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3007,6 +3007,7 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, if (Arg *A = Args.getLastArg(options::OPT_fno_stack_protector, options::OPT_fstack_protector_all, + options::OPT_fstack_protector_guard, options::OPT_fstack_protector_strong, options::OPT_fstack_protector)) { if (A->getOption().matches(options::OPT_fstack_protector)) @@ -3014,6 +3015,8 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, std::max<>(LangOptions::SSPOn, DefaultStackProtectorLevel); else if (A->getOption().matches(options::OPT_fstack_protector_strong)) StackProtectorLevel = LangOptions::SSPStrong; + else if (A->getOption().matches(options::OPT_fstack_protector_guard)) + StackProtectorLevel = LangOptions::SSPGuard; else if (A->getOption().matches(options::OPT_fstack_protector_all)) StackProtectorLevel = LangOptions::SSPReq; } else { @@ -5636,7 +5639,48 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_mno_speculative_load_hardening, false)) CmdArgs.push_back(Args.MakeArgString("-mspeculative-load-hardening")); - RenderSSPOptions(D, TC, Args, CmdArgs, KernelOrKext); + // -ret-protector + unsigned RetProtector = 0; + if (Arg *A = Args.getLastArg(options::OPT_fno_ret_protector, + options::OPT_fret_protector)) { + if (A->getOption().matches(options::OPT_fno_ret_protector)) + RetProtector = 0; + else if (A->getOption().matches(options::OPT_fret_protector)) + RetProtector = 1; + } + + if (RetProtector && + ((getToolChain().getArch() == llvm::Triple::x86_64) || + (getToolChain().getArch() == llvm::Triple::mips64) || + (getToolChain().getArch() == llvm::Triple::mips64el) || + (getToolChain().getArch() == llvm::Triple::ppc) || + (getToolChain().getArch() == llvm::Triple::ppc64) || + (getToolChain().getArch() == llvm::Triple::ppc64le) || + (getToolChain().getArch() == llvm::Triple::aarch64)) && + !Args.hasArg(options::OPT_fno_stack_protector) && + !Args.hasArg(options::OPT_pg)) { + CmdArgs.push_back(Args.MakeArgString("-D_RET_PROTECTOR")); + CmdArgs.push_back(Args.MakeArgString("-ret-protector")); + // Consume the stack protector arguments to prevent warning + Args.getLastArg(options::OPT_fstack_protector_all, + options::OPT_fstack_protector_strong, + options::OPT_fstack_protector, + options::OPT__param); // ssp-buffer-size + } else { + // If we're not using retguard, then do the usual stack protector + RenderSSPOptions(D, TC, Args, CmdArgs, KernelOrKext); + } + + // -fixup-gadgets + if (Arg *A = Args.getLastArg(options::OPT_fno_fixup_gadgets, + options::OPT_ffixup_gadgets)) { + CmdArgs.push_back(Args.MakeArgString(Twine("-mllvm"))); + if (A->getOption().matches(options::OPT_fno_fixup_gadgets)) + CmdArgs.push_back(Args.MakeArgString(Twine("-x86-fixup-gadgets=false"))); + else if (A->getOption().matches(options::OPT_ffixup_gadgets)) + CmdArgs.push_back(Args.MakeArgString(Twine("-x86-fixup-gadgets=true"))); + } + RenderSCPOptions(TC, Args, CmdArgs); RenderTrivialAutoVarInitOptions(D, TC, Args, CmdArgs); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 5c5cf46150e2..5993ee4003d0 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1178,6 +1178,8 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, Opts.LinkBitcodeFiles.push_back(F); } + Opts.ReturnProtector = Args.hasArg(OPT_ret_protector); + if (Args.getLastArg(OPT_femulated_tls) || Args.getLastArg(OPT_fno_emulated_tls)) { Opts.ExplicitEmulatedTLS = true; diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index c64a912ce919..114f1676f670 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -1038,6 +1038,8 @@ static void InitializePredefinedMacros(const TargetInfo &TI, Builder.defineMacro("__SSP_STRONG__", "2"); else if (LangOpts.getStackProtector() == LangOptions::SSPReq) Builder.defineMacro("__SSP_ALL__", "3"); + else if (LangOpts.getStackProtector() == LangOptions::SSPGuard) + Builder.defineMacro("__SSP_GUARD__", "4"); if (PPOpts.SetUpStaticAnalyzer) Builder.defineMacro("__clang_analyzer__"); diff --git a/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp b/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp index 36e7b90cad24..8e68f75020b0 100644 --- a/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp +++ b/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp @@ -870,6 +870,18 @@ int16_t x86AssemblyInspectionEngine::extract_2_signed(uint8_t *b) { return v; } +// movq $0x????????(%rip), $reg [(0x4c || 0x48) 0x8b ?? ?? ?? ?? ??] +// xorq $off(%rsp), $reg [(0x4c || 0x48) 0x33 ?? 0x24] +bool x86AssemblyInspectionEngine::retguard_prologue_p(size_t offset, int insn_len) { + uint8_t *p = m_cur_insn; + if (offset == 0 && insn_len == 7) + return (*p == 0x48 || *p == 0x4c) && (*(p + 1) == 0x8b); + else if (offset == 7 && insn_len == 4) + return (*p == 0x48 || *p == 0x4c) && (*(p + 1) == 0x33) && (*(p + 3) == 0x24); + + return false; +} + uint32_t x86AssemblyInspectionEngine::extract_4(uint8_t *b) { uint32_t v = 0; for (int i = 3; i >= 0; i--) @@ -1601,6 +1613,7 @@ bool x86AssemblyInspectionEngine::FindFirstNonPrologueInstruction( if (push_rbp_pattern_p() || mov_rsp_rbp_pattern_p() || sub_rsp_pattern_p(scratch) || push_reg_p(regno) || mov_reg_to_local_stack_frame_p(regno, scratch) || + retguard_prologue_p(offset, insn_len) || (lea_rsp_pattern_p(scratch) && offset == 0)) { offset += insn_len; continue; diff --git a/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h b/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h index f39dce1afaa6..d0b6f65e9e1d 100644 --- a/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h +++ b/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h @@ -114,6 +114,7 @@ private: bool call_next_insn_pattern_p(); bool mov_reg_to_local_stack_frame_p(int ®no, int &rbp_offset); bool ret_pattern_p(); + bool retguard_prologue_p(size_t offset, int insn_len); bool jmp_to_reg_p(); bool pc_rel_branch_or_jump_p (const int instruction_length, int &offset); bool non_local_branch_p (const lldb::addr_t current_func_text_offset, diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h index 7f0ec0df57c5..1e20db0707d7 100644 --- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h +++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h @@ -274,6 +274,15 @@ private: /// The frame index for the stack protector. int StackProtectorIdx = -1; + struct ReturnProtector { + /// The register to use for return protector calculations + unsigned Register = 0; + /// Set to true if this function needs return protectors + bool Needed = false; + /// Does the return protector cookie need to be stored in frame + bool NeedsStore = true; + } RPI; + /// The frame index for the function context. Used for SjLj exceptions. int FunctionContextIdx = -1; @@ -355,6 +364,17 @@ public: void setStackProtectorIndex(int I) { StackProtectorIdx = I; } bool hasStackProtectorIndex() const { return StackProtectorIdx != -1; } + /// Get / Set return protector calculation register + unsigned getReturnProtectorRegister() const { return RPI.Register; } + void setReturnProtectorRegister(unsigned I) { RPI.Register = I; } + bool hasReturnProtectorRegister() const { return RPI.Register != 0; } + /// Get / Set if this frame needs a return protector + void setReturnProtectorNeeded(bool I) { RPI.Needed = I; } + bool getReturnProtectorNeeded() const { return RPI.Needed; } + /// Get / Set if the return protector cookie needs to be stored in frame + void setReturnProtectorNeedsStore(bool I) { RPI.NeedsStore = I; } + bool getReturnProtectorNeedsStore() const { return RPI.NeedsStore; } + /// 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/Passes.h b/llvm/include/llvm/CodeGen/Passes.h index 676ed2c65eb1..51413e063642 100644 --- a/llvm/include/llvm/CodeGen/Passes.h +++ b/llvm/include/llvm/CodeGen/Passes.h @@ -334,6 +334,9 @@ namespace llvm { /// FunctionPass *createStackProtectorPass(); + // createReturnProtectorPass - This pass add return protectors to functions. + FunctionPass *createReturnProtectorPass(); + /// createMachineVerifierPass - This pass verifies cenerated machine code /// instructions for correctness. /// diff --git a/llvm/include/llvm/CodeGen/ReturnProtectorLowering.h b/llvm/include/llvm/CodeGen/ReturnProtectorLowering.h new file mode 100644 index 000000000000..5e0b4c818cda --- /dev/null +++ b/llvm/include/llvm/CodeGen/ReturnProtectorLowering.h @@ -0,0 +1,79 @@ +//===-- llvm/CodeGen/ReturnProtectorLowering.h ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A class to insert and lower the return protector instrumentation +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_RETURNPROTECTORLOWERING_H +#define LLVM_CODEGEN_RETURNPROTECTORLOWERING_H + +#include "llvm/ADT/SmallVector.h" + +#include +#include + +namespace llvm { +class CalleeSavedInfo; +class GlobalVariable; +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; + +class ReturnProtectorLowering { +public: + virtual ~ReturnProtectorLowering() {} + /// Subclass interface - subclasses need to implement these functions. + + /// insertReturnProtectorPrologue/Epilogue - insert return protector + /// instrumentation in prologue or epilogue. + virtual void insertReturnProtectorPrologue(MachineFunction &MF, + MachineBasicBlock &MBB, + GlobalVariable *cookie) const {} + virtual void insertReturnProtectorEpilogue(MachineFunction &MF, + MachineInstr &MI, + GlobalVariable *cookie) const {} + + /// opcodeIsReturn - Reuturn true is the given opcode is a return + /// instruction needing return protection, false otherwise. + virtual bool opcodeIsReturn(unsigned opcode) const { return false; } + + /// fillTempRegisters - Fill the list of available temp registers we can + /// use as a CalculationRegister. + virtual void fillTempRegisters(MachineFunction &MF, + std::vector &TempRegs) const {} + + /// Generic public interface used by llvm + + /// setupReturnProtector - Checks the function for ROP friendly return + /// instructions and sets ReturnProtectorNeeded in the frame if found. + virtual void setupReturnProtector(MachineFunction &MF) const; + + /// saveReturnProtectorRegister - Allows the target to save the + /// CalculationRegister in the CalleeSavedInfo vector if needed. + virtual void + saveReturnProtectorRegister(MachineFunction &MF, + std::vector &CSI) const; + + /// determineReturnProtectorTempRegister - Find a register that can be used + /// during function prologue / epilogue to store the return protector cookie. + /// Returns false if a register is needed but could not be found, + /// otherwise returns true. + virtual bool determineReturnProtectorRegister( + MachineFunction &MF, + const SmallVector &SaveBlocks, + const SmallVector &RestoreBlocks) const; + + /// insertReturnProtectors - insert return protector instrumentation. + virtual void insertReturnProtectors(MachineFunction &MF) const; +}; + +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/CodeGen/StackProtector.h b/llvm/include/llvm/CodeGen/StackProtector.h index f6513e8d4ea0..6aea6ee32522 100644 --- a/llvm/include/llvm/CodeGen/StackProtector.h +++ b/llvm/include/llvm/CodeGen/StackProtector.h @@ -101,6 +101,8 @@ private: /// stack protector based upon the stack protector level. bool RequiresStackProtector(); + bool CreateRetGuardCookie(); + public: static char ID; // Pass identification, replacement for typeid. diff --git a/llvm/include/llvm/CodeGen/TargetFrameLowering.h b/llvm/include/llvm/CodeGen/TargetFrameLowering.h index 792452f6e81d..35af2a23728f 100644 --- a/llvm/include/llvm/CodeGen/TargetFrameLowering.h +++ b/llvm/include/llvm/CodeGen/TargetFrameLowering.h @@ -14,6 +14,7 @@ #define LLVM_CODEGEN_TARGETFRAMELOWERING_H #include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/ReturnProtectorLowering.h" #include "llvm/Support/TypeSize.h" #include @@ -209,6 +210,10 @@ public: virtual void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const = 0; + virtual const ReturnProtectorLowering *getReturnProtector() const { + return nullptr; + } + /// With basic block sections, emit callee saved frame moves for basic blocks /// that are in a different section. virtual void diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index f7ffc888c65a..4d9cccbeb2d4 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -209,6 +209,9 @@ def StackProtectReq : EnumAttr<"sspreq">; /// Strong Stack protection. def StackProtectStrong : EnumAttr<"sspstrong">; +/// Strong Stack guard. +def StackProtectGuard : EnumAttr<"sspguard">; + /// Function was called in a scope requiring strict floating point semantics. def StrictFP : EnumAttr<"strictfp">; diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h index 4f89179a03de..cf8bb6981edc 100644 --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -381,6 +381,7 @@ void initializeRegionViewerPass(PassRegistry&); void initializeRegisterCoalescerPass(PassRegistry&); void initializeRenameIndependentSubregsPass(PassRegistry&); void initializeResetMachineFunctionPass(PassRegistry&); +void initializeReturnProtectorPass(PassRegistry&); void initializeReversePostOrderFunctionAttrsLegacyPassPass(PassRegistry&); void initializeRewriteStatepointsForGCLegacyPassPass(PassRegistry &); void initializeRewriteSymbolsLegacyPassPass(PassRegistry&); diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h index 5149f861837a..40c2ad1ea82f 100644 --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -230,6 +230,7 @@ enum Kind { kw_ssp, kw_sspreq, kw_sspstrong, + kw_sspguard, kw_safestack, kw_shadowcallstack, kw_sret, diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index 32a7946af63b..62fb9210fc4b 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -148,6 +148,8 @@ add_llvm_component_library(LLVMCodeGen RegUsageInfoCollector.cpp RegUsageInfoPropagate.cpp ResetMachineFunctionPass.cpp + ReturnProtectorLowering.cpp + ReturnProtectorPass.cpp SafeStack.cpp SafeStackLayout.cpp ScheduleDAG.cpp diff --git a/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp b/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp index ec6e693e8a46..e9826f7ad038 100644 --- a/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp +++ b/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp @@ -30,6 +30,7 @@ #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" +#include "llvm/IR/Function.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -197,7 +198,12 @@ void LocalStackSlotPass::calculateFrameObjectOffsets(MachineFunction &Fn) { // Make sure that the stack protector comes before the local variables on the // stack. + Function &F = Fn.getFunction(); SmallSet ProtectedObjs; + StackObjSet LargeArrayObjs; + StackObjSet SmallArrayObjs; + StackObjSet AddrOfObjs; + if (MFI.hasStackProtectorIndex()) { int StackProtectorFI = MFI.getStackProtectorIndex(); @@ -208,10 +214,6 @@ void LocalStackSlotPass::calculateFrameObjectOffsets(MachineFunction &Fn) { assert(!MFI.isObjectPreAllocated(StackProtectorFI) && "Stack protector pre-allocated in LocalStackSlotAllocation"); - StackObjSet LargeArrayObjs; - StackObjSet SmallArrayObjs; - StackObjSet AddrOfObjs; - AdjustStackOffset(MFI, StackProtectorFI, Offset, StackGrowsDown, MaxAlign); // Assign large stack objects first. @@ -238,15 +240,37 @@ void LocalStackSlotPass::calculateFrameObjectOffsets(MachineFunction &Fn) { } llvm_unreachable("Unexpected SSPLayoutKind."); } - - AssignProtectedObjSet(LargeArrayObjs, ProtectedObjs, MFI, StackGrowsDown, - Offset, MaxAlign); - AssignProtectedObjSet(SmallArrayObjs, ProtectedObjs, MFI, StackGrowsDown, - Offset, MaxAlign); - AssignProtectedObjSet(AddrOfObjs, ProtectedObjs, MFI, StackGrowsDown, - Offset, MaxAlign); + } else if (F.hasFnAttribute(Attribute::StackProtectGuard)) { + // Assign large stack objects first. + for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) { + if (MFI.isDeadObjectIndex(i)) + continue; + if (!TFI.isStackIdSafeForLocalArea(MFI.getStackID(i))) + continue; + switch (MFI.getObjectSSPLayout(i)) { + case MachineFrameInfo::SSPLK_None: + continue; + case MachineFrameInfo::SSPLK_SmallArray: + SmallArrayObjs.insert(i); + continue; + case MachineFrameInfo::SSPLK_AddrOf: + AddrOfObjs.insert(i); + continue; + case MachineFrameInfo::SSPLK_LargeArray: + LargeArrayObjs.insert(i); + continue; + } + llvm_unreachable("Unexpected SSPLayoutKind."); + } } + AssignProtectedObjSet(LargeArrayObjs, ProtectedObjs, MFI, StackGrowsDown, + Offset, MaxAlign); + AssignProtectedObjSet(SmallArrayObjs, ProtectedObjs, MFI, StackGrowsDown, + Offset, MaxAlign); + AssignProtectedObjSet(AddrOfObjs, ProtectedObjs, MFI, StackGrowsDown, + Offset, MaxAlign); + // Then assign frame offsets to stack objects that are not used to spill // callee saved registers. for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) { diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp index 378aaba2a65f..f9ca383260fc 100644 --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -217,6 +217,10 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) { const Function &F = MF.getFunction(); const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering(); + const ReturnProtectorLowering *RPL = TFI->getReturnProtector(); + + if (RPL) + RPL->setupReturnProtector(MF); RS = TRI->requiresRegisterScavenging(MF) ? new RegScavenger() : nullptr; FrameIndexVirtualScavenging = TRI->requiresFrameIndexScavenging(MF); @@ -255,6 +259,10 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) { if (!F.hasFnAttribute(Attribute::Naked)) insertPrologEpilogCode(MF); + // Add Return Protectors if using them + if (RPL) + RPL->insertReturnProtectors(MF); + // Reinsert stashed debug values at the start of the entry blocks. for (auto &I : EntryDbgValues) I.first->insert(I.first->begin(), I.second.begin(), I.second.end()); @@ -353,7 +361,9 @@ void PEI::calculateCallFrameInfo(MachineFunction &MF) { /// Compute the sets of entry and return blocks for saving and restoring /// callee-saved registers, and placing prolog and epilog code. void PEI::calculateSaveRestoreBlocks(MachineFunction &MF) { - const MachineFrameInfo &MFI = MF.getFrameInfo(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering(); + const ReturnProtectorLowering *RPL = TFI->getReturnProtector(); // Even when we do not change any CSR, we still want to insert the // prologue and epilogue of the function. @@ -369,7 +379,18 @@ void PEI::calculateSaveRestoreBlocks(MachineFunction &MF) { // epilogue. if (!RestoreBlock->succ_empty() || RestoreBlock->isReturnBlock()) RestoreBlocks.push_back(RestoreBlock); - return; + + // If we are adding return protectors ensure we can find a free register + if (RPL && + !RPL->determineReturnProtectorRegister(MF, SaveBlocks, RestoreBlocks)) { + // Shrinkwrapping will prevent finding a free register + SaveBlocks.clear(); + RestoreBlocks.clear(); + MFI.setSavePoint(nullptr); + MFI.setRestorePoint(nullptr); + } else { + return; + } } // Save refs to entry and return blocks. @@ -380,6 +401,9 @@ void PEI::calculateSaveRestoreBlocks(MachineFunction &MF) { if (MBB.isReturnBlock()) RestoreBlocks.push_back(&MBB); } + + if (RPL) + RPL->determineReturnProtectorRegister(MF, SaveBlocks, RestoreBlocks); } static void assignCalleeSavedSpillSlots(MachineFunction &F, @@ -401,6 +425,10 @@ static void assignCalleeSavedSpillSlots(MachineFunction &F, const TargetFrameLowering *TFI = F.getSubtarget().getFrameLowering(); MachineFrameInfo &MFI = F.getFrameInfo(); + + if (TFI->getReturnProtector()) + TFI->getReturnProtector()->saveReturnProtectorRegister(F, CSI); + if (!TFI->assignCalleeSavedSpillSlots(F, RegInfo, CSI)) { // If target doesn't implement this, use generic code. @@ -926,13 +954,14 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &MF) { // Make sure that the stack protector comes before the local variables on the // stack. + Function &F = MF.getFunction(); + StackObjSet LargeArrayObjs; + StackObjSet SmallArrayObjs; + StackObjSet AddrOfObjs; + SmallSet ProtectedObjs; if (MFI.hasStackProtectorIndex()) { int StackProtectorFI = MFI.getStackProtectorIndex(); - StackObjSet LargeArrayObjs; - StackObjSet SmallArrayObjs; - StackObjSet AddrOfObjs; - // If we need a stack protector, we need to make sure that // LocalStackSlotPass didn't already allocate a slot for it. // If we are told to use the LocalStackAllocationBlock, the stack protector @@ -984,15 +1013,47 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &MF) { AddrOfObjs.empty())) llvm_unreachable("Found protected stack objects not pre-allocated by " "LocalStackSlotPass."); + } else if (F.hasFnAttribute(Attribute::StackProtectGuard)) { + // Assign large stack objects first. + for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) { + if (MFI.isObjectPreAllocated(i) && MFI.getUseLocalStackAllocationBlock()) + continue; + if (i >= MinCSFrameIndex && i <= MaxCSFrameIndex) + continue; + if (RS && RS->isScavengingFrameIndex((int)i)) + continue; + if (MFI.isDeadObjectIndex(i)) + continue; + if (EHRegNodeFrameIndex == (int)i) + continue; + if (MFI.getStackID(i) != + TargetStackID::Default) // Only allocate objects on the default stack. + continue; - AssignProtectedObjSet(LargeArrayObjs, ProtectedObjs, MFI, StackGrowsDown, - Offset, MaxAlign, Skew); - AssignProtectedObjSet(SmallArrayObjs, ProtectedObjs, MFI, StackGrowsDown, - Offset, MaxAlign, Skew); - AssignProtectedObjSet(AddrOfObjs, ProtectedObjs, MFI, StackGrowsDown, - Offset, MaxAlign, Skew); + switch (MFI.getObjectSSPLayout(i)) { + case MachineFrameInfo::SSPLK_None: + continue; + case MachineFrameInfo::SSPLK_SmallArray: + SmallArrayObjs.insert(i); + continue; + case MachineFrameInfo::SSPLK_AddrOf: + AddrOfObjs.insert(i); + continue; + case MachineFrameInfo::SSPLK_LargeArray: + LargeArrayObjs.insert(i); + continue; + } + llvm_unreachable("Unexpected SSPLayoutKind."); + } } + AssignProtectedObjSet(LargeArrayObjs, ProtectedObjs, MFI, StackGrowsDown, + Offset, MaxAlign, Skew); + AssignProtectedObjSet(SmallArrayObjs, ProtectedObjs, MFI, StackGrowsDown, + Offset, MaxAlign, Skew); + AssignProtectedObjSet(AddrOfObjs, ProtectedObjs, MFI, StackGrowsDown, + Offset, MaxAlign, Skew); + SmallVector ObjectsToAllocate; // Then prepare to assign frame offsets to stack objects that are not used to diff --git a/llvm/lib/CodeGen/ReturnProtectorLowering.cpp b/llvm/lib/CodeGen/ReturnProtectorLowering.cpp new file mode 100644 index 000000000000..f03e657a5384 --- /dev/null +++ b/llvm/lib/CodeGen/ReturnProtectorLowering.cpp @@ -0,0 +1,340 @@ +//===- ReturnProtectorLowering.cpp - ---------------------------------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements common routines for return protector support. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/ReturnProtectorLowering.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +using namespace llvm; + +static void markUsedRegsInSuccessors(MachineBasicBlock &MBB, + SmallSet &Used, + SmallSet &Visited) { + int BBNum = MBB.getNumber(); + if (Visited.count(BBNum)) + return; + + // Mark all the registers used + for (auto &MBBI : MBB.instrs()) { + for (auto &MBBIOp : MBBI.operands()) { + if (MBBIOp.isReg()) + Used.insert(MBBIOp.getReg()); + } + } + + // Mark this MBB as visited + Visited.insert(BBNum); + // Recurse over all successors + for (auto &SuccMBB : MBB.successors()) + markUsedRegsInSuccessors(*SuccMBB, Used, Visited); +} + +static bool containsProtectableData(Type *Ty) { + if (!Ty) + return false; + + if (ArrayType *AT = dyn_cast(Ty)) + return true; + + if (StructType *ST = dyn_cast(Ty)) { + for (StructType::element_iterator I = ST->element_begin(), + E = ST->element_end(); + I != E; ++I) { + if (containsProtectableData(*I)) + return true; + } + } + return false; +} + +// Mostly the same as StackProtector::HasAddressTaken +static bool hasAddressTaken(const Instruction *AI, + SmallPtrSet &visitedPHI) { + for (const User *U : AI->users()) { + const auto *I = cast(U); + switch (I->getOpcode()) { + case Instruction::Store: + if (AI == cast(I)->getValueOperand()) + return true; + break; + case Instruction::AtomicCmpXchg: + if (AI == cast(I)->getNewValOperand()) + return true; + break; + case Instruction::PtrToInt: + if (AI == cast(I)->getOperand(0)) + return true; + break; + case Instruction::BitCast: + case Instruction::GetElementPtr: + case Instruction::Select: + case Instruction::AddrSpaceCast: + if (hasAddressTaken(I, visitedPHI)) + return true; + break; + case Instruction::PHI: { + const auto *PN = cast(I); + if (visitedPHI.insert(PN).second) + if (hasAddressTaken(PN, visitedPHI)) + return true; + break; + } + case Instruction::Load: + case Instruction::AtomicRMW: + case Instruction::Ret: + return false; + break; + default: + // Conservatively return true for any instruction that takes an address + // operand, but is not handled above. + return true; + } + } + return false; +} + +/// setupReturnProtector - Checks the function for ROP friendly return +/// instructions and sets ReturnProtectorNeeded if found. +void ReturnProtectorLowering::setupReturnProtector(MachineFunction &MF) const { + if (MF.getFunction().hasFnAttribute("ret-protector") + || MF.getFunction().hasFnAttribute(Attribute::StackProtectGuard)) { + for (auto &MBB : MF) { + for (auto &T : MBB.terminators()) { + if (opcodeIsReturn(T.getOpcode())) { + MF.getFrameInfo().setReturnProtectorNeeded(true); + return; + } + } + } + } +} + +/// saveReturnProtectorRegister - Allows the target to save the +/// ReturnProtectorRegister in the CalleeSavedInfo vector if needed. +void ReturnProtectorLowering::saveReturnProtectorRegister( + MachineFunction &MF, std::vector &CSI) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + if (!MFI.getReturnProtectorNeeded()) + return; + + if (!MFI.hasReturnProtectorRegister()) + llvm_unreachable("Saving unset return protector register"); + + unsigned Reg = MFI.getReturnProtectorRegister(); + if (MFI.getReturnProtectorNeedsStore()) + CSI.push_back(CalleeSavedInfo(Reg)); + else { + for (auto &MBB : MF) { + if (!MBB.isLiveIn(Reg)) + MBB.addLiveIn(Reg); + } + } +} + +/// determineReturnProtectorTempRegister - Find a register that can be used +/// during function prologue / epilogue to store the return protector cookie. +/// Returns false if a register is needed but could not be found, +/// otherwise returns true. +bool ReturnProtectorLowering::determineReturnProtectorRegister( + MachineFunction &MF, const SmallVector &SaveBlocks, + const SmallVector &RestoreBlocks) const { + MachineFrameInfo &MFI = MF.getFrameInfo(); + if (!MFI.getReturnProtectorNeeded()) + return true; + + const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); + + std::vector TempRegs; + fillTempRegisters(MF, TempRegs); + + // For leaf functions, try to find a free register that is available + // in every BB, so we do not need to store it in the frame at all. + // We walk the entire function here because MFI.hasCalls() is unreliable. + bool hasCalls = false; + for (auto &MBB : MF) { + for (auto &MI : MBB) { + if (MI.isCall() && !MI.isReturn()) { + hasCalls = true; + break; + } + } + if (hasCalls) + break; + } + + // If the return address is always on the stack, then we + // want to try to keep the return protector cookie unspilled. + // This prevents a single stack smash from corrupting both the + // return protector cookie and the return address. + llvm::Triple::ArchType arch = MF.getTarget().getTargetTriple().getArch(); + bool returnAddrOnStack = arch == llvm::Triple::ArchType::x86 + || arch == llvm::Triple::ArchType::x86_64; + + // For architectures which do not spill a return address + // to the stack by default, it is possible that in a leaf + // function that neither the return address or the retguard cookie + // will be spilled, and stack corruption may be missed. + // Here, we check leaf functions on these kinds of architectures + // to see if they have any variable sized local allocations, + // array type allocations, allocations which contain array + // types, or elements that have their address taken. If any of + // these conditions are met, then we skip leaf function + // optimization and spill the retguard cookie to the stack. + bool hasLocals = MFI.hasVarSizedObjects(); + if (!hasCalls && !hasLocals && !returnAddrOnStack) { + for (const BasicBlock &BB : MF.getFunction()) { + for (const Instruction &I : BB) { + if (const AllocaInst *AI = dyn_cast(&I)) { + // Check for array allocations + Type *Ty = AI->getAllocatedType(); + if (AI->isArrayAllocation() || containsProtectableData(Ty)) { + hasLocals = true; + break; + } + // Check for address taken + SmallPtrSet visitedPHIs; + if (hasAddressTaken(AI, visitedPHIs)) { + hasLocals = true; + break; + } + } + } + if (hasLocals) + break; + } + } + + bool tryLeafOptimize = !hasCalls && (returnAddrOnStack || !hasLocals); + + if (tryLeafOptimize) { + SmallSet LeafUsed; + SmallSet LeafVisited; + markUsedRegsInSuccessors(MF.front(), LeafUsed, LeafVisited); + for (unsigned Reg : TempRegs) { + bool canUse = true; + for (MCRegAliasIterator AI(Reg, TRI, true); AI.isValid(); ++AI) { + if (LeafUsed.count(*AI)) { + canUse = false; + break; + } + } + if (canUse) { + MFI.setReturnProtectorRegister(Reg); + MFI.setReturnProtectorNeedsStore(false); + return true; + } + } + } + + // For non-leaf functions, we only need to search save / restore blocks + SmallSet Used; + SmallSet Visited; + + // CSR spills happen at the beginning of this block + // so we can mark it as visited because anything past it is safe + for (auto &SB : SaveBlocks) + Visited.insert(SB->getNumber()); + + // CSR Restores happen at the end of restore blocks, before any terminators, + // so we need to search restores for MBB terminators, and any successor BBs. + for (auto &RB : RestoreBlocks) { + for (auto &RBI : RB->terminators()) { + for (auto &RBIOp : RBI.operands()) { + if (RBIOp.isReg()) + Used.insert(RBIOp.getReg()); + } + } + for (auto &SuccMBB : RB->successors()) + markUsedRegsInSuccessors(*SuccMBB, Used, Visited); + } + + // Now we iterate from the front to find code paths that + // bypass save blocks and land on return blocks + markUsedRegsInSuccessors(MF.front(), Used, Visited); + + // Now we have gathered all the regs used outside the frame save / restore, + // so we can see if we have a free reg to use for the retguard cookie. + for (unsigned Reg : TempRegs) { + bool canUse = true; + for (MCRegAliasIterator AI(Reg, TRI, true); AI.isValid(); ++AI) { + if (Used.count(*AI)) { + // Reg is used somewhere, so we cannot use it + canUse = false; + break; + } + } + if (canUse) { + MFI.setReturnProtectorRegister(Reg); + break; + } + } + + return MFI.hasReturnProtectorRegister(); +} + +/// insertReturnProtectors - insert return protector instrumentation. +void ReturnProtectorLowering::insertReturnProtectors( + MachineFunction &MF) const { + MachineFrameInfo &MFI = MF.getFrameInfo(); + + if (!MFI.getReturnProtectorNeeded()) + return; + + if (!MFI.hasReturnProtectorRegister()) + llvm_unreachable("Inconsistent return protector state."); + + const Function &Fn = MF.getFunction(); + const Module *M = Fn.getParent(); + GlobalVariable *cookie = + dyn_cast_or_null(M->getGlobalVariable( + Fn.getFnAttribute("ret-protector-cookie").getValueAsString(), + Type::getInt8PtrTy(M->getContext()))); + + if (!cookie) + llvm_unreachable("Function needs return protector but no cookie assigned"); + + unsigned Reg = MFI.getReturnProtectorRegister(); + + std::vector returns; + for (auto &MBB : MF) { + if (MBB.isReturnBlock()) { + for (auto &MI : MBB.terminators()) { + if (opcodeIsReturn(MI.getOpcode())) { + returns.push_back(&MI); + if (!MBB.isLiveIn(Reg)) + MBB.addLiveIn(Reg); + } + } + } + } + + if (returns.empty()) + return; + + for (auto &MI : returns) + insertReturnProtectorEpilogue(MF, *MI, cookie); + + insertReturnProtectorPrologue(MF, MF.front(), cookie); + + if (!MF.front().isLiveIn(Reg)) + MF.front().addLiveIn(Reg); +} diff --git a/llvm/lib/CodeGen/ReturnProtectorPass.cpp b/llvm/lib/CodeGen/ReturnProtectorPass.cpp new file mode 100644 index 000000000000..1259354df720 --- /dev/null +++ b/llvm/lib/CodeGen/ReturnProtectorPass.cpp @@ -0,0 +1,63 @@ +//===- ReturnProtectorPass.cpp - Set up rteurn protectors -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass sets up functions for return protectors. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/Passes.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "return-protector" + +STATISTIC(NumSymbols, "Counts number of cookie symbols added"); + +namespace { + struct ReturnProtector : public FunctionPass { + static char ID; + ReturnProtector() : FunctionPass(ID) {} + + bool runOnFunction(Function &F) override { + if (F.hasFnAttribute("ret-protector")) { + // Create a symbol for the cookie + Module *M = F.getParent(); + std::hash hasher; + std::string hash = std::to_string(hasher((M->getName() + F.getName()).str()) % 4000); + std::string cookiename = "__retguard_" + hash; + Type *cookietype = Type::getInt8PtrTy(M->getContext()); + GlobalVariable *cookie = dyn_cast_or_null( + M->getOrInsertGlobal(cookiename, cookietype)); + cookie->setInitializer(Constant::getNullValue(cookietype)); + cookie->setLinkage(GlobalVariable::LinkOnceAnyLinkage); + cookie->setVisibility(GlobalValue::HiddenVisibility); + cookie->setComdat(M->getOrInsertComdat(cookiename)); + cookie->setSection(".openbsd.randomdata.retguard." + hash); + cookie->setExternallyInitialized(true); + F.addFnAttr("ret-protector-cookie", cookiename); + NumSymbols++; + } + return false; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + } + }; +} + +char ReturnProtector::ID = 0; +INITIALIZE_PASS(ReturnProtector, "return-protector", "Return Protector Pass", + false, false) +FunctionPass *llvm::createReturnProtectorPass() { return new ReturnProtector(); } diff --git a/llvm/lib/CodeGen/SafeStack.cpp b/llvm/lib/CodeGen/SafeStack.cpp index 31797631c97b..416f7ca9425a 100644 --- a/llvm/lib/CodeGen/SafeStack.cpp +++ b/llvm/lib/CodeGen/SafeStack.cpp @@ -809,6 +809,7 @@ bool SafeStack::run() { // FIXME: implement weaker forms of stack protector. if (F.hasFnAttribute(Attribute::StackProtect) || F.hasFnAttribute(Attribute::StackProtectStrong) || + F.hasFnAttribute(Attribute::StackProtectGuard) || F.hasFnAttribute(Attribute::StackProtectReq)) { Value *StackGuard = getStackGuard(IRB, F); StackGuardSlot = IRB.CreateAlloca(StackPtrTy, nullptr); diff --git a/llvm/lib/CodeGen/StackProtector.cpp b/llvm/lib/CodeGen/StackProtector.cpp index 10c6dcbdb049..e870e631555d 100644 --- a/llvm/lib/CodeGen/StackProtector.cpp +++ b/llvm/lib/CodeGen/StackProtector.cpp @@ -97,6 +97,8 @@ bool StackProtector::runOnFunction(Function &Fn) { Attr.getValueAsString().getAsInteger(10, SSPBufferSize)) return false; // Invalid integer string + CreateRetGuardCookie(); + if (!RequiresStackProtector()) return false; @@ -108,10 +110,36 @@ bool StackProtector::runOnFunction(Function &Fn) { return false; } + if (Fn.hasFnAttribute(Attribute::StackProtectGuard)) { + HasIRCheck = true; + return false; + } + ++NumFunProtected; return InsertStackProtectors(); } +bool StackProtector::CreateRetGuardCookie() +{ + std::hash hasher; + std::string hash = std::to_string(hasher((M->getName() + F->getName()).str()) % 4000); + std::string cookiename = "__retguard_" + hash; + + Type *cookietype = Type::getInt8PtrTy(M->getContext()); + GlobalVariable *cookie = dyn_cast_or_null(M->getOrInsertGlobal(cookiename, cookietype)); + + cookie->setInitializer(Constant::getNullValue(cookietype)); + cookie->setLinkage(GlobalVariable::LinkOnceAnyLinkage); + cookie->setVisibility(GlobalValue::HiddenVisibility); + cookie->setComdat(M->getOrInsertComdat(cookiename)); + cookie->setSection(".openbsd.randomdata.retguard." + hash); + cookie->setExternallyInitialized(true); + + F->addFnAttr("ret-protector-cookie", cookiename); + + return true; +} + /// \param [out] IsLarge is set to true if a protectable array is found and /// it is "large" ( >= ssp-buffer-size). In the case of a structure with /// multiple arrays, this gets set if any of them is large. @@ -294,6 +322,8 @@ bool StackProtector::RequiresStackProtector() { Strong = true; // Use the same heuristic as strong to determine SSPLayout } else if (F->hasFnAttribute(Attribute::StackProtectStrong)) Strong = true; + else if (F->hasFnAttribute(Attribute::StackProtectGuard)) + Strong = true; else if (!F->hasFnAttribute(Attribute::StackProtect)) return false; diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp index e844d03854e2..3867d428080a 100644 --- a/llvm/lib/CodeGen/TargetPassConfig.cpp +++ b/llvm/lib/CodeGen/TargetPassConfig.cpp @@ -935,6 +935,8 @@ void TargetPassConfig::addISelPrepare() { // Force codegen to run according to the callgraph. if (requiresCodeGenSCCOrder()) addPass(new DummyCGSCCPass); + + addPass(createReturnProtectorPass()); // Add both the safe stack and the stack protection passes: each of them will // only protect functions that have corresponding attributes. diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index c4629decc6d9..d4bfa680821f 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -1966,6 +1966,7 @@ static void adjustCallerSSPLevel(Function &Caller, const Function &Callee) { AttrBuilder OldSSPAttr; OldSSPAttr.addAttribute(Attribute::StackProtect) .addAttribute(Attribute::StackProtectStrong) + .addAttribute(Attribute::StackProtectGuard) .addAttribute(Attribute::StackProtectReq); if (Callee.hasFnAttribute(Attribute::StackProtectReq)) { @@ -1977,8 +1978,15 @@ static void adjustCallerSSPLevel(Function &Caller, const Function &Callee) { Caller.addFnAttr(Attribute::StackProtectStrong); } else if (Callee.hasFnAttribute(Attribute::StackProtect) && !Caller.hasFnAttribute(Attribute::StackProtectReq) && - !Caller.hasFnAttribute(Attribute::StackProtectStrong)) + !Caller.hasFnAttribute(Attribute::StackProtectStrong)) { + Caller.removeAttributes(AttributeList::FunctionIndex, OldSSPAttr); Caller.addFnAttr(Attribute::StackProtect); + } else if (Callee.hasFnAttribute(Attribute::StackProtectGuard) && + !Caller.hasFnAttribute(Attribute::StackProtect) && + !Caller.hasFnAttribute(Attribute::StackProtectReq) && + !Caller.hasFnAttribute(Attribute::StackProtectStrong)) { + Caller.addFnAttr(Attribute::StackProtectGuard); + } } /// If the inlined function required stack probes, then ensure that diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index 17247123f87f..d0a7d0de9102 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -615,6 +615,7 @@ void Function::clearGC() { bool Function::hasStackProtectorFnAttr() const { return hasFnAttribute(Attribute::StackProtect) || hasFnAttribute(Attribute::StackProtectStrong) || + hasFnAttribute(Attribute::StackProtectGuard) || hasFnAttribute(Attribute::StackProtectReq); } diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index a0c5498ee620..66ffa18ebc59 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -1455,6 +1455,19 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { case AArch64::SEH_EpilogEnd: TS->EmitARM64WinCFIEpilogEnd(); return; + + case AArch64::RETGUARD_JMP_TRAP: + { + MCSymbol *RGSuccSym = OutContext.createTempSymbol(); + /* Compare and branch */ + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::CBZX) + .addReg(MI->getOperand(0).getReg()) + .addExpr(MCSymbolRefExpr::create(RGSuccSym, OutContext))); + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::BRK).addImm(1)); + OutStreamer->emitLabel(RGSuccSym); + return; + } + } // Finally, do the automated lowerings for everything else. diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index 65ee5016042c..03a8ebc6646f 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -116,6 +116,7 @@ #include "AArch64InstrInfo.h" #include "AArch64MachineFunctionInfo.h" #include "AArch64RegisterInfo.h" +#include "AArch64ReturnProtectorLowering.h" #include "AArch64Subtarget.h" #include "AArch64TargetMachine.h" #include "MCTargetDesc/AArch64AddressingModes.h" @@ -2560,6 +2561,10 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, ? RegInfo->getBaseRegister() : (unsigned)AArch64::NoRegister; + if (MFI.hasReturnProtectorRegister() && MFI.getReturnProtectorNeedsStore()) { + SavedRegs.set(MFI.getReturnProtectorRegister()); + } + unsigned ExtraCSSpill = 0; // Figure out which callee-saved registers to save/restore. for (unsigned i = 0; CSRegs[i]; ++i) { @@ -3307,6 +3312,10 @@ unsigned AArch64FrameLowering::getWinEHFuncletFrameSize( getStackAlign()); } +const ReturnProtectorLowering *AArch64FrameLowering::getReturnProtector() const { + return &RPL; +} + namespace { struct FrameObject { bool IsValid = false; diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.h b/llvm/lib/Target/AArch64/AArch64FrameLowering.h index 80079a9d9836..d4ea4239fcda 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.h +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.h @@ -13,6 +13,7 @@ #ifndef LLVM_LIB_TARGET_AARCH64_AARCH64FRAMELOWERING_H #define LLVM_LIB_TARGET_AARCH64_AARCH64FRAMELOWERING_H +#include "AArch64ReturnProtectorLowering.h" #include "llvm/Support/TypeSize.h" #include "llvm/CodeGen/TargetFrameLowering.h" @@ -22,9 +23,12 @@ class MCCFIInstruction; class AArch64FrameLowering : public TargetFrameLowering { public: + + const AArch64ReturnProtectorLowering RPL; + explicit AArch64FrameLowering() : TargetFrameLowering(StackGrowsDown, Align(16), 0, Align(16), - true /*StackRealignable*/) {} + true /*StackRealignable*/), RPL() {} void emitCalleeSavedFrameMoves(MachineBasicBlock &MBB, @@ -39,6 +43,8 @@ public: void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + const ReturnProtectorLowering *getReturnProtector() const override; + bool canUseAsPrologue(const MachineBasicBlock &MBB) const override; StackOffset getFrameIndexReference(const MachineFunction &MF, int FI, diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 171d3dbaa814..3d81981923f4 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -704,6 +704,14 @@ def ADDlowTLS } // isReMaterializable, isCodeGenOnly +//===----------------------------------------------------------------------===// +// Pseudo instruction used by retguard +let isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in { + let Size = 8 in { + def RETGUARD_JMP_TRAP: Pseudo<(outs), (ins GPR64:$reg), []>; + } +} + def : Pat<(AArch64LOADgot tglobaltlsaddr:$addr), (LOADgot tglobaltlsaddr:$addr)>; diff --git a/llvm/lib/Target/AArch64/AArch64ReturnProtectorLowering.cpp b/llvm/lib/Target/AArch64/AArch64ReturnProtectorLowering.cpp new file mode 100644 index 000000000000..5d97ae002830 --- /dev/null +++ b/llvm/lib/Target/AArch64/AArch64ReturnProtectorLowering.cpp @@ -0,0 +1,124 @@ +//===-- AArch64ReturnProtectorLowering.cpp --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the AArch64 implementation of ReturnProtectorLowering +// class. +// +//===----------------------------------------------------------------------===// + +#include "AArch64InstrInfo.h" +#include "AArch64MachineFunctionInfo.h" +#include "AArch64RegisterInfo.h" +#include "AArch64ReturnProtectorLowering.h" +#include "AArch64Subtarget.h" +#include "AArch64TargetMachine.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetOptions.h" +#include + +using namespace llvm; + +void AArch64ReturnProtectorLowering::insertReturnProtectorPrologue( + MachineFunction &MF, MachineBasicBlock &MBB, GlobalVariable *cookie) const { + + MachineBasicBlock::instr_iterator MI = MBB.instr_begin(); + DebugLoc MBBDL = MBB.findDebugLoc(MI); + const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + unsigned REG = MF.getFrameInfo().getReturnProtectorRegister(); + + BuildMI(MBB, MI, MBBDL, TII->get(AArch64::ADRP), REG) + .addGlobalAddress(cookie, 0, AArch64II::MO_PAGE); + BuildMI(MBB, MI, MBBDL, TII->get(AArch64::LDRXui), REG) + .addReg(REG) + .addGlobalAddress(cookie, 0, AArch64II::MO_PAGEOFF | AArch64II::MO_NC); + BuildMI(MBB, MI, MBBDL, TII->get(AArch64::EORXrr), REG) + .addReg(REG) + .addReg(AArch64::LR); +} + +void AArch64ReturnProtectorLowering::insertReturnProtectorEpilogue( + MachineFunction &MF, MachineInstr &MI, GlobalVariable *cookie) const { + + MachineBasicBlock &MBB = *MI.getParent(); + DebugLoc MBBDL = MI.getDebugLoc(); + const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + unsigned REG = MF.getFrameInfo().getReturnProtectorRegister(); + + MBB.addLiveIn(AArch64::X9); + // REG holds the cookie we calculated in prologue. We use X9 as a + // scratch reg to pull the random data. XOR REG with LR should yield + // the random data again. Compare REG with X9 to check. + BuildMI(MBB, MI, MBBDL, TII->get(AArch64::EORXrr), REG) + .addReg(REG) + .addReg(AArch64::LR); + BuildMI(MBB, MI, MBBDL, TII->get(AArch64::ADRP), AArch64::X9) + .addGlobalAddress(cookie, 0, AArch64II::MO_PAGE); + BuildMI(MBB, MI, MBBDL, TII->get(AArch64::LDRXui), AArch64::X9) + .addReg(AArch64::X9) + .addGlobalAddress(cookie, 0, AArch64II::MO_PAGEOFF | AArch64II::MO_NC); + BuildMI(MBB, MI, MBBDL, TII->get(AArch64::SUBSXrr), REG) + .addReg(REG) + .addReg(AArch64::X9); + BuildMI(MBB, MI, MBBDL, TII->get(AArch64::RETGUARD_JMP_TRAP)).addReg(REG); +} + +bool AArch64ReturnProtectorLowering::opcodeIsReturn(unsigned opcode) const { + switch (opcode) { + case AArch64::RET: + case AArch64::RET_ReallyLR: + return true; + default: + return false; + } +} + +void AArch64ReturnProtectorLowering::fillTempRegisters( + MachineFunction &MF, std::vector &TempRegs) const { + + TempRegs.push_back(AArch64::X15); + TempRegs.push_back(AArch64::X14); + TempRegs.push_back(AArch64::X13); + TempRegs.push_back(AArch64::X12); + TempRegs.push_back(AArch64::X11); + TempRegs.push_back(AArch64::X10); +} + +void AArch64ReturnProtectorLowering::saveReturnProtectorRegister( + MachineFunction &MF, std::vector &CSI) const { + + const MachineFrameInfo &MFI = MF.getFrameInfo(); + if (!MFI.getReturnProtectorNeeded()) + return; + + if (!MFI.hasReturnProtectorRegister()) + llvm_unreachable("Saving unset return protector register"); + + unsigned Reg = MFI.getReturnProtectorRegister(); + if (!MFI.getReturnProtectorNeedsStore()) { + for (auto &MBB : MF) { + if (!MBB.isLiveIn(Reg)) + MBB.addLiveIn(Reg); + } + return; + } + + // CSI Reg order is important for pairing registers later. + // The expected order of the CSI is given by getCalleeSavedRegs(), + // which for us returns a list of GPRs and FPRs in ascending + // order. Since our temp regs are all before the usual callee + // saved regs, we can just insert our reg first. + CSI.insert(CSI.begin(), CalleeSavedInfo(MFI.getReturnProtectorRegister())); +} diff --git a/llvm/lib/Target/AArch64/AArch64ReturnProtectorLowering.h b/llvm/lib/Target/AArch64/AArch64ReturnProtectorLowering.h new file mode 100644 index 000000000000..9537b4643f22 --- /dev/null +++ b/llvm/lib/Target/AArch64/AArch64ReturnProtectorLowering.h @@ -0,0 +1,52 @@ +//===-- AArch64ReturnProtectorLowering.h - --------------------- -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the AArch64 implementation of ReturnProtectorLowering +// class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AARCH64_AARCH64RETURNPROTECTORLOWERING_H +#define LLVM_LIB_TARGET_AARCH64_AARCH64RETURNPROTECTORLOWERING_H + +#include "llvm/CodeGen/ReturnProtectorLowering.h" + +namespace llvm { + +class AArch64ReturnProtectorLowering : public ReturnProtectorLowering { +public: + /// insertReturnProtectorPrologue/Epilogue - insert return protector + /// instrumentation in prologue or epilogue. + virtual void + insertReturnProtectorPrologue(MachineFunction &MF, MachineBasicBlock &MBB, + GlobalVariable *cookie) const override; + virtual void + insertReturnProtectorEpilogue(MachineFunction &MF, MachineInstr &MI, + GlobalVariable *cookie) const override; + + /// opcodeIsReturn - Reuturn true is the given opcode is a return + /// instruction needing return protection, false otherwise. + virtual bool opcodeIsReturn(unsigned opcode) const override; + + /// fillTempRegisters - Fill the list of available temp registers we can + /// use as a return protector register. + virtual void + fillTempRegisters(MachineFunction &MF, + std::vector &TempRegs) const override; + + /// saveReturnProtectorRegister - Allows the target to save the + /// CalculationRegister in the CalleeSavedInfo vector if needed. + virtual void + saveReturnProtectorRegister(MachineFunction &MF, + std::vector &CSI) const override; +}; + +} // namespace llvm + +#endif diff --git a/llvm/lib/Target/AArch64/CMakeLists.txt b/llvm/lib/Target/AArch64/CMakeLists.txt index 0e9503baf180..28620cee7113 100644 --- a/llvm/lib/Target/AArch64/CMakeLists.txt +++ b/llvm/lib/Target/AArch64/CMakeLists.txt @@ -65,6 +65,7 @@ add_llvm_target(AArch64CodeGen AArch64PromoteConstant.cpp AArch64PBQPRegAlloc.cpp AArch64RegisterInfo.cpp + AArch64ReturnProtectorLowering.cpp AArch64SLSHardening.cpp AArch64SelectionDAGInfo.cpp AArch64SpeculationHardening.cpp diff --git a/llvm/lib/Target/X86/CMakeLists.txt b/llvm/lib/Target/X86/CMakeLists.txt index 5a5002670296..a9bef49785d0 100644 --- a/llvm/lib/Target/X86/CMakeLists.txt +++ b/llvm/lib/Target/X86/CMakeLists.txt @@ -38,6 +38,7 @@ set(sources X86ExpandPseudo.cpp X86FastISel.cpp X86FixupBWInsts.cpp + X86FixupGadgets.cpp X86FixupLEAs.cpp X86AvoidStoreForwardingBlocks.cpp X86FixupSetCC.cpp @@ -67,6 +68,7 @@ set(sources X86PartialReduction.cpp X86RegisterBankInfo.cpp X86RegisterInfo.cpp + X86ReturnProtectorLowering.cpp X86SelectionDAGInfo.cpp X86ShuffleDecodeConstantPool.cpp X86SpeculativeLoadHardening.cpp diff --git a/llvm/lib/Target/X86/X86.h b/llvm/lib/Target/X86/X86.h index e17b9ba5500b..ce65cea927d7 100644 --- a/llvm/lib/Target/X86/X86.h +++ b/llvm/lib/Target/X86/X86.h @@ -114,6 +114,10 @@ FunctionPass *createX86FixupBWInsts(); /// to another, when profitable. FunctionPass *createX86DomainReassignmentPass(); +/// Return a Machine Function pass that attempts to replace +/// ROP friendly instructions with alternatives. +FunctionPass *createX86FixupGadgetsPass(); + /// This pass replaces EVEX encoded of AVX-512 instructiosn by VEX /// encoding when possible in order to reduce code size. FunctionPass *createX86EvexToVexInsts(); diff --git a/llvm/lib/Target/X86/X86FixupGadgets.cpp b/llvm/lib/Target/X86/X86FixupGadgets.cpp new file mode 100644 index 000000000000..6610d425b83d --- /dev/null +++ b/llvm/lib/Target/X86/X86FixupGadgets.cpp @@ -0,0 +1,708 @@ +//===-- X86FixupGadgets.cpp - Fixup Instructions that make ROP Gadgets ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file defines a function pass that checks instructions for sequences +/// that will lower to a potentially useful ROP gadget, and attempts to +/// replace those sequences with alternatives that are not useful for ROP. +/// +//===----------------------------------------------------------------------===// + +#include "X86.h" +#include "X86InstrBuilder.h" +#include "X86InstrInfo.h" +#include "X86MachineFunctionInfo.h" +#include "X86Subtarget.h" +#include "X86TargetMachine.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define FIXUPGADGETS_DESC "X86 ROP Gadget Fixup" +#define FIXUPGADGETS_NAME "x86-fixup-gadgets" + +#define DEBUG_TYPE FIXUPGADGETS_NAME + +// Toggle with cc1 option: -mllvm -x86-fixup-gadgets= +static cl::opt FixupGadgets( + "x86-fixup-gadgets", cl::Hidden, + cl::desc("Replace ROP friendly instructions with safe alternatives"), + cl::init(true)); + +namespace { +class FixupGadgetsPass : public MachineFunctionPass { + +public: + static char ID; + + StringRef getPassName() const override { return FIXUPGADGETS_DESC; } + + FixupGadgetsPass() + : MachineFunctionPass(ID), STI(nullptr), TII(nullptr), TRI(nullptr) {} + + /// Loop over all the instructions and replace ROP friendly + /// seuqences with less ROP friendly alternatives + bool runOnMachineFunction(MachineFunction &MF) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + +private: + const X86Subtarget *STI; + const X86InstrInfo *TII; + const X86RegisterInfo *TRI; + bool Is64Bit; + + struct FixupInfo { + unsigned op1; + unsigned op2; + bool fixup; + bool align; + }; + + uint8_t getRegNum(const MachineOperand &MO) const; + uint8_t getRegNum(unsigned reg) const; + struct FixupInfo isROPFriendly(MachineInstr &MI) const; + bool isROPFriendlyImm(const MachineOperand &MO) const; + bool isROPFriendlyRegPair(const MachineOperand &Dst, + const MachineOperand &Src) const; + bool isROPFriendlyReg(const MachineOperand &Dst, uint8_t RegOpcode) const; + bool badModRM(uint8_t Mod, uint8_t RegOpcode, uint8_t RM) const; + void checkSIB(const MachineInstr &MI, unsigned CurOp, + struct FixupInfo &info) const; + bool needsFixup(struct FixupInfo &fi) const; + bool needsAlign(struct FixupInfo &fi) const; + unsigned getWidestRegForReg(unsigned reg) const; + unsigned getEquivalentRegForReg(unsigned oreg, unsigned nreg) const; + bool hasImplicitUseOrDef(const MachineInstr &MI, unsigned Reg1, + unsigned Reg2) const; + bool fixupWithoutExchange(MachineInstr &MI); + + bool fixupInstruction(MachineFunction &MF, MachineBasicBlock &MBB, + MachineInstr &MI, struct FixupInfo Info); +}; +char FixupGadgetsPass::ID = 0; +} // namespace + +FunctionPass *llvm::createX86FixupGadgetsPass() { + return new FixupGadgetsPass(); +} + +uint8_t FixupGadgetsPass::getRegNum(const MachineOperand &MO) const { + return TRI->getEncodingValue(MO.getReg()) & 0x7; +} + +uint8_t FixupGadgetsPass::getRegNum(unsigned reg) const { + return TRI->getEncodingValue(reg) & 0x7; +} + +bool FixupGadgetsPass::isROPFriendlyImm(const MachineOperand &MO) const { + int64_t imm = MO.getImm(); + for (int i = 0; i < 8; ++i) { + uint8_t byte = (imm & 0xff); + if (byte == 0xc2 || byte == 0xc3 || byte == 0xca || byte == 0xcb) { + return true; + } + imm = imm >> 8; + } + return false; +} + +bool FixupGadgetsPass::isROPFriendlyRegPair(const MachineOperand &Dst, + const MachineOperand &Src) const { + + if (!Dst.isReg() || !Src.isReg()) + llvm_unreachable("Testing non registers for bad reg pair!"); + + uint8_t Mod = 3; + uint8_t RegOpcode = getRegNum(Src); + uint8_t RM = getRegNum(Dst); + return badModRM(Mod, RegOpcode, RM); +} + +bool FixupGadgetsPass::isROPFriendlyReg(const MachineOperand &Dst, uint8_t RegOpcode) const { + + if (!Dst.isReg()) + llvm_unreachable("Testing non register for bad reg!"); + + uint8_t Mod = 3; + uint8_t RM = getRegNum(Dst); + return badModRM(Mod, RegOpcode, RM); +} + +bool FixupGadgetsPass::badModRM(uint8_t Mod, uint8_t RegOpcode, + uint8_t RM) const { + uint8_t ModRM = ((Mod << 6) | (RegOpcode << 3) | RM); + if (ModRM == 0xc2 || ModRM == 0xc3 || ModRM == 0xca || ModRM == 0xcb) + return true; + return false; +} + +void FixupGadgetsPass::checkSIB(const MachineInstr &MI, unsigned CurOp, + struct FixupInfo &info) const { + + const MachineOperand &Base = MI.getOperand(CurOp + X86::AddrBaseReg); + const MachineOperand &Scale = MI.getOperand(CurOp + X86::AddrScaleAmt); + const MachineOperand &Index = MI.getOperand(CurOp + X86::AddrIndexReg); + + if (!Scale.isImm() || !Base.isReg() || !Index.isReg()) + llvm_unreachable("Wrong type operands"); + + if (Scale.getImm() != 8 || Base.getReg() == 0 || Index.getReg() == 0) + return; + + if (badModRM(3, getRegNum(Index), getRegNum(Base))) { + info.op1 = CurOp + X86::AddrBaseReg; + info.op2 = CurOp + X86::AddrIndexReg; + info.fixup = true; + } +} + +struct FixupGadgetsPass::FixupInfo +FixupGadgetsPass::isROPFriendly(MachineInstr &MI) const { + + const MCInstrDesc &Desc = MI.getDesc(); + unsigned CurOp = X86II::getOperandBias(Desc); + uint64_t TSFlags = Desc.TSFlags; + uint64_t Form = TSFlags & X86II::FormMask; + bool HasVEX_4V = TSFlags & X86II::VEX_4V; + bool HasEVEX_K = TSFlags & X86II::EVEX_K; + + struct FixupInfo info = {0, 0, false, false}; + + // Look for constants with c3 in them + for (const auto &MO : MI.operands()) { + if (MO.isImm() && isROPFriendlyImm(MO)) { + info.align = true; + break; + } + } + + switch (Form) { + case X86II::Pseudo: { + // Pesudos that are replaced with real instructions later + switch (MI.getOpcode()) { + case X86::ADD64rr_DB: + case X86::ADD32rr_DB: + case X86::ADD16rr_DB: + goto Handle_MRMDestReg; + case X86::ADD16ri_DB: + case X86::ADD32ri_DB: + case X86::ADD64ri32_DB: + case X86::ADD16ri8_DB: + case X86::ADD32ri8_DB: + case X86::ADD64ri8_DB: + goto Handle_MRMXr; + default: + break; + } + break; + } + case X86II::AddRegFrm: { + uint8_t BaseOpcode = X86II::getBaseOpcodeFor(TSFlags); + uint8_t Opcode = BaseOpcode + getRegNum(MI.getOperand(CurOp)); + if (Opcode == 0xc2 || Opcode == 0xc3 || Opcode == 0xca || Opcode == 0xcb) { + info.op1 = CurOp; + info.fixup = true; + } + break; + } + case X86II::MRMDestMem: { + checkSIB(MI, CurOp, info); + unsigned opcode = MI.getOpcode(); + if (opcode == X86::MOVNTImr || opcode == X86::MOVNTI_64mr) + info.align = true; + break; + } + case X86II::MRMSrcMem: { + CurOp += 1; + if (HasVEX_4V) + CurOp += 1; + if (HasEVEX_K) + CurOp += 1; + checkSIB(MI, CurOp, info); + break; + } + case X86II::MRMSrcMem4VOp3: { + CurOp += 1; + checkSIB(MI, CurOp, info); + break; + } + case X86II::MRMSrcMemOp4: { + CurOp += 3; + checkSIB(MI, CurOp, info); + break; + } + case X86II::MRMXm: + case X86II::MRM0m: + case X86II::MRM1m: + case X86II::MRM2m: + case X86II::MRM3m: + case X86II::MRM4m: + case X86II::MRM5m: + case X86II::MRM6m: + case X86II::MRM7m: { + if (HasVEX_4V) + CurOp += 1; + if (HasEVEX_K) + CurOp += 1; + checkSIB(MI, CurOp, info); + break; + } + case X86II::MRMDestReg: { + Handle_MRMDestReg: + const MachineOperand &DstReg = MI.getOperand(CurOp); + info.op1 = CurOp; + CurOp += 1; + if (HasVEX_4V) + CurOp += 1; + if (HasEVEX_K) + CurOp += 1; + const MachineOperand &SrcReg = MI.getOperand(CurOp); + info.op2 = CurOp; + if (isROPFriendlyRegPair(DstReg, SrcReg)) + info.fixup = true; + break; + } + case X86II::MRMSrcReg: { + const MachineOperand &DstReg = MI.getOperand(CurOp); + info.op1 = CurOp; + CurOp += 1; + if (HasVEX_4V) + CurOp += 1; + if (HasEVEX_K) + CurOp += 1; + const MachineOperand &SrcReg = MI.getOperand(CurOp); + info.op2 = CurOp; + if (isROPFriendlyRegPair(SrcReg, DstReg)) + info.fixup = true; + break; + } + case X86II::MRMSrcReg4VOp3: { + const MachineOperand &DstReg = MI.getOperand(CurOp); + info.op1 = CurOp; + CurOp += 1; + const MachineOperand &SrcReg = MI.getOperand(CurOp); + info.op2 = CurOp; + if (isROPFriendlyRegPair(SrcReg, DstReg)) + info.fixup = true; + break; + } + case X86II::MRMSrcRegOp4: { + const MachineOperand &DstReg = MI.getOperand(CurOp); + info.op1 = CurOp; + CurOp += 3; + const MachineOperand &SrcReg = MI.getOperand(CurOp); + info.op2 = CurOp; + if (isROPFriendlyRegPair(SrcReg, DstReg)) + info.fixup = true; + break; + } + case X86II::MRMXr: + case X86II::MRM0r: + case X86II::MRM1r: { +Handle_MRMXr: + if (HasVEX_4V) + CurOp += 1; + if (HasEVEX_K) + CurOp += 1; + const MachineOperand &DstReg = MI.getOperand(CurOp); + info.op1 = CurOp; + if (isROPFriendlyReg(DstReg, Form == X86II::MRM1r ? 1 : 0)) + info.fixup = true; + break; + } + case X86II::MRM_C2: + case X86II::MRM_C3: + case X86II::MRM_CA: + case X86II::MRM_CB: { + info.align = true; + break; + } + default: + break; + } + return info; +} + +bool FixupGadgetsPass::needsFixup(struct FixupInfo &fi) const { + return (fi.fixup == true); +} + +bool FixupGadgetsPass::needsAlign(struct FixupInfo &fi) const { + return (fi.align == true); +} + +unsigned FixupGadgetsPass::getWidestRegForReg(unsigned reg) const { + + switch (reg) { + case X86::AL: + case X86::AH: + case X86::AX: + case X86::EAX: + case X86::RAX: + return Is64Bit ? X86::RAX : X86::EAX; + case X86::BL: + case X86::BH: + case X86::BX: + case X86::EBX: + case X86::RBX: + return Is64Bit ? X86::RBX : X86::EBX; + case X86::CL: + case X86::CH: + case X86::CX: + case X86::ECX: + case X86::RCX: + return Is64Bit ? X86::RCX : X86::ECX; + case X86::DL: + case X86::DH: + case X86::DX: + case X86::EDX: + case X86::RDX: + return Is64Bit ? X86::RDX : X86::EDX; + case X86::R8B: + case X86::R8W: + case X86::R8D: + case X86::R8: + return X86::R8; + case X86::R9B: + case X86::R9W: + case X86::R9D: + case X86::R9: + return X86::R9; + case X86::R10B: + case X86::R10W: + case X86::R10D: + case X86::R10: + return X86::R10; + case X86::R11B: + case X86::R11W: + case X86::R11D: + case X86::R11: + return X86::R11; + default: + return X86::NoRegister; // Non-GP Reg + } + return 0; +} + +// For given register oreg return the equivalent size register +// from the nreg register set. Eg. For oreg ebx and nreg ax, return eax. +unsigned FixupGadgetsPass::getEquivalentRegForReg(unsigned oreg, + unsigned nreg) const { + unsigned compreg = getWidestRegForReg(nreg); + + switch (oreg) { + case X86::AL: + case X86::BL: + case X86::CL: + case X86::DL: + case X86::R8B: + case X86::R9B: + case X86::R10B: + case X86::R11B: + switch (compreg) { + case X86::EAX: + case X86::RAX: + return X86::AL; + case X86::EBX: + case X86::RBX: + return X86::BL; + case X86::ECX: + case X86::RCX: + return X86::CL; + case X86::EDX: + case X86::RDX: + return X86::DL; + case X86::R8: + return X86::R8B; + case X86::R9: + return X86::R9B; + case X86::R10: + return X86::R10B; + case X86::R11: + return X86::R11B; + default: + llvm_unreachable("Unknown 8 bit register"); + } + break; + case X86::AH: + case X86::BH: + case X86::CH: + case X86::DH: + switch (compreg) { + case X86::EAX: + return X86::AH; + case X86::EBX: + return X86::BH; + case X86::ECX: + return X86::CH; + case X86::EDX: + return X86::DH; + default: + llvm_unreachable("Using H registers in REX mode"); + } + break; + case X86::AX: + case X86::BX: + case X86::CX: + case X86::DX: + case X86::R8W: + case X86::R9W: + case X86::R10W: + case X86::R11W: + switch (compreg) { + case X86::EAX: + case X86::RAX: + return X86::AX; + case X86::EBX: + case X86::RBX: + return X86::BX; + case X86::ECX: + case X86::RCX: + return X86::CX; + case X86::EDX: + case X86::RDX: + return X86::DX; + case X86::R8: + return X86::R8W; + case X86::R9: + return X86::R9W; + case X86::R10: + return X86::R10W; + case X86::R11: + return X86::R11W; + default: + llvm_unreachable("Unknown 16 bit register"); + } + break; + case X86::EAX: + case X86::EBX: + case X86::ECX: + case X86::EDX: + case X86::R8D: + case X86::R9D: + case X86::R10D: + case X86::R11D: + switch (compreg) { + case X86::EAX: + case X86::RAX: + return X86::EAX; + case X86::EBX: + case X86::RBX: + return X86::EBX; + case X86::ECX: + case X86::RCX: + return X86::ECX; + case X86::EDX: + case X86::RDX: + return X86::EDX; + case X86::R8: + return X86::R8D; + case X86::R9: + return X86::R9D; + case X86::R10: + return X86::R10D; + case X86::R11: + return X86::R11D; + default: + llvm_unreachable("Unknown 32 bit register"); + } + break; + case X86::RAX: + case X86::RBX: + case X86::RCX: + case X86::RDX: + case X86::R8: + case X86::R9: + case X86::R10: + case X86::R11: + return compreg; + default: + llvm_unreachable("Unknown input register!"); + } +} + +bool FixupGadgetsPass::hasImplicitUseOrDef(const MachineInstr &MI, + unsigned Reg1, unsigned Reg2) const { + + const MCInstrDesc &Desc = MI.getDesc(); + + const MCPhysReg *ImpDefs = Desc.getImplicitDefs(); + if (ImpDefs) { + for (; *ImpDefs; ++ImpDefs) { + unsigned w = getWidestRegForReg(*ImpDefs); + if (w == Reg1 || w == Reg2) { + return true; + } + } + } + + const MCPhysReg *ImpUses = Desc.getImplicitUses(); + if (ImpUses) { + for (; *ImpUses; ++ImpUses) { + unsigned w = getWidestRegForReg(*ImpUses); + if (w == Reg1 || w == Reg2) { + return true; + } + } + } + return false; +} + +bool FixupGadgetsPass::fixupWithoutExchange(MachineInstr &MI) { + switch (MI.getOpcode()) { + case X86::MOV8rr_REV: + MI.setDesc(TII->get(X86::MOV8rr)); + break; + case X86::MOV16rr_REV: + MI.setDesc(TII->get(X86::MOV16rr)); + break; + case X86::MOV32rr_REV: + MI.setDesc(TII->get(X86::MOV32rr)); + break; + case X86::MOV64rr_REV: + MI.setDesc(TII->get(X86::MOV64rr)); + break; + case X86::MOV8rr: + MI.setDesc(TII->get(X86::MOV8rr_REV)); + break; + case X86::MOV16rr: + MI.setDesc(TII->get(X86::MOV16rr_REV)); + break; + case X86::MOV32rr: + MI.setDesc(TII->get(X86::MOV32rr_REV)); + break; + case X86::MOV64rr: + MI.setDesc(TII->get(X86::MOV64rr_REV)); + break; + default: + return false; + } + return true; +} + +bool FixupGadgetsPass::fixupInstruction(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineInstr &MI, FixupInfo Info) { + + if (!needsAlign(Info) && !needsFixup(Info)) + return false; + + DebugLoc DL = MI.getDebugLoc(); + + // Check for only needs alignment + if (needsAlign(Info) && !needsFixup(Info)) { + BuildMI(MBB, MI, DL, TII->get(X86::JMP_TRAP)); + return true; + } + + unsigned XCHG = Is64Bit ? X86::XCHG64rr : X86::XCHG32rr; + + unsigned OrigReg1 = MI.getOperand(Info.op1).getReg(); + // Swap with RAX/EAX unless we have a second register to swap with + unsigned OrigReg2 = Is64Bit ? X86::RAX : X86::EAX; + if (Info.op2) + OrigReg2 = MI.getOperand(Info.op2).getReg(); + + unsigned SwapReg1 = getWidestRegForReg(OrigReg1); + unsigned SwapReg2 = getWidestRegForReg(OrigReg2); + unsigned CompReg1 = SwapReg1; + unsigned CompReg2 = SwapReg2; + + // Just align if: + // - we have a non-GP reg to swap with + // - the instruction implicitly uses one of the registers we are swapping + // - if we are fixing an instruction that skips the xchg back + if (SwapReg1 == X86::NoRegister || SwapReg2 == X86::NoRegister || + hasImplicitUseOrDef(MI, CompReg1, CompReg2) || MI.isCall() || + MI.isReturn() || MI.isBranch() || MI.isIndirectBranch() || + MI.isBarrier()) { + BuildMI(MBB, MI, DL, TII->get(X86::JMP_TRAP)); + return true; + } + + // Make sure our XCHG doesn't make a gadget + if (badModRM(3, getRegNum(SwapReg1), getRegNum(SwapReg2))) { + unsigned treg = SwapReg1; + SwapReg1 = SwapReg2; + SwapReg2 = treg; + } + + // Check for specific instructions we can fix without the xchg dance + if (fixupWithoutExchange(MI)) { + return true; + } + + // Swap the two registers to start + BuildMI(MBB, MI, DL, TII->get(XCHG)) + .addReg(SwapReg1, RegState::Define) + .addReg(SwapReg2, RegState::Define) + .addReg(SwapReg1).addReg(SwapReg2); + + // Check for needs alignment + if (needsAlign(Info)) + BuildMI(MBB, MI, DL, TII->get(X86::JMP_TRAP)); + + // Swap the registers inside the instruction + for (MachineOperand &MO : MI.operands()) { + if (!MO.isReg()) + continue; + + unsigned reg = MO.getReg(); + unsigned match = getWidestRegForReg(reg); + if (match == CompReg1) + MO.setReg(getEquivalentRegForReg(reg, OrigReg2)); + else if (match == CompReg2) + MO.setReg(getEquivalentRegForReg(reg, OrigReg1)); + } + + // And swap the two registers back + BuildMI(MBB, ++MachineBasicBlock::instr_iterator(MI), DL, TII->get(XCHG)) + .addReg(SwapReg1, RegState::Define) + .addReg(SwapReg2, RegState::Define) + .addReg(SwapReg1).addReg(SwapReg2); + + return true; +} + +bool FixupGadgetsPass::runOnMachineFunction(MachineFunction &MF) { + if (!FixupGadgets) + return false; + + STI = &MF.getSubtarget(); + TII = STI->getInstrInfo(); + TRI = STI->getRegisterInfo(); + Is64Bit = STI->is64Bit(); + std::vector> fixups; + FixupInfo info; + + bool modified = false; + + for (auto &MBB : MF) { + fixups.clear(); + for (auto &MI : MBB) { + info = isROPFriendly(MI); + if (needsAlign(info) || needsFixup(info)) + fixups.push_back(std::make_pair(&MI, info)); + } + for (auto &fixup : fixups) + modified |= fixupInstruction(MF, MBB, *fixup.first, fixup.second); + } + + return modified; +} diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp index 866f11364004..ca54a2049a1e 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -14,6 +14,7 @@ #include "X86InstrBuilder.h" #include "X86InstrInfo.h" #include "X86MachineFunctionInfo.h" +#include "X86ReturnProtectorLowering.h" #include "X86Subtarget.h" #include "X86TargetMachine.h" #include "llvm/ADT/SmallSet.h" @@ -46,7 +47,7 @@ X86FrameLowering::X86FrameLowering(const X86Subtarget &STI, MaybeAlign StackAlignOverride) : TargetFrameLowering(StackGrowsDown, StackAlignOverride.valueOrOne(), STI.is64Bit() ? -8 : -4), - STI(STI), TII(*STI.getInstrInfo()), TRI(STI.getRegisterInfo()) { + STI(STI), TII(*STI.getInstrInfo()), TRI(STI.getRegisterInfo()), RPL() { // Cache a bunch of frame-related predicates for this subtarget. SlotSize = TRI->getSlotSize(); Is64Bit = STI.is64Bit(); @@ -3575,6 +3576,10 @@ void X86FrameLowering::adjustFrameForMsvcCxxEh(MachineFunction &MF) const { .addImm(-2); } +const ReturnProtectorLowering *X86FrameLowering::getReturnProtector() const { + return &RPL; +} + void X86FrameLowering::processFunctionBeforeFrameIndicesReplaced( MachineFunction &MF, RegScavenger *RS) const { if (STI.is32Bit() && MF.hasEHFunclets()) diff --git a/llvm/lib/Target/X86/X86FrameLowering.h b/llvm/lib/Target/X86/X86FrameLowering.h index 26e80811af2e..384fc110e209 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.h +++ b/llvm/lib/Target/X86/X86FrameLowering.h @@ -13,6 +13,7 @@ #ifndef LLVM_LIB_TARGET_X86_X86FRAMELOWERING_H #define LLVM_LIB_TARGET_X86_X86FRAMELOWERING_H +#include "X86ReturnProtectorLowering.h" #include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/Support/TypeSize.h" @@ -23,6 +24,7 @@ class MCCFIInstruction; class X86InstrInfo; class X86Subtarget; class X86RegisterInfo; +class X86ReturnProtectorLowering; class X86FrameLowering : public TargetFrameLowering { public: @@ -33,6 +35,7 @@ public: const X86Subtarget &STI; const X86InstrInfo &TII; const X86RegisterInfo *TRI; + const X86ReturnProtectorLowering RPL; unsigned SlotSize; @@ -73,6 +76,8 @@ public: void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + const ReturnProtectorLowering *getReturnProtector() const override; + void adjustForSegmentedStacks(MachineFunction &MF, MachineBasicBlock &PrologueMBB) const override; diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td index dc6361aecc60..66d7b741aeec 100644 --- a/llvm/lib/Target/X86/X86InstrCompiler.td +++ b/llvm/lib/Target/X86/X86InstrCompiler.td @@ -274,6 +274,25 @@ def MORESTACK_RET: I<0, Pseudo, (outs), (ins), "", []>; def MORESTACK_RET_RESTORE_R10 : I<0, Pseudo, (outs), (ins), "", []>; } +//===----------------------------------------------------------------------===// +// Pseudo instruction used by retguard + +// This is lowered to a JE 2; INT3; INT3. Prior to this pseudo should be a +// compare instruction to ensure the retguard cookie is correct. +// We use a pseudo here in order to avoid splitting the BB just before the return. +// Splitting the BB and inserting a JE_1 over a new INT3 BB occasionally +// resulted in incorrect code when a value from a byte register (CL) was +// used as a return value. When emitted as a split BB, the single byte +// register would sometimes be widened to 4 bytes, which would corrupt +// the return value (ie mov %ecx, %eax instead of mov %cl, %al). +let isCodeGenOnly = 1, hasNoSchedulingInfo = 1, Uses = [EFLAGS] in { +def RETGUARD_JMP_TRAP: I<0, Pseudo, (outs), (ins), "", []>; +} + +let isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in { +def JMP_TRAP: I<0, Pseudo, (outs), (ins), "", []>; +} + //===----------------------------------------------------------------------===// // Alias Instructions //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp index 89fa3ae3a3f4..e3eaf044b80c 100644 --- a/llvm/lib/Target/X86/X86MCInstLower.cpp +++ b/llvm/lib/Target/X86/X86MCInstLower.cpp @@ -2570,6 +2570,50 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { MCInstBuilder(X86::MOV64rr).addReg(X86::R10).addReg(X86::RAX)); return; + case X86::RETGUARD_JMP_TRAP: { + // Make a symbol for the end of the trapsled and emit a jump to it + MCSymbol *RGSuccSym = OutContext.createTempSymbol(); + const MCExpr *RGSuccExpr = MCSymbolRefExpr::create(RGSuccSym, OutContext); + EmitAndCountInstruction(MCInstBuilder(X86::JCC_1) + .addExpr(RGSuccExpr) + .addImm(X86::COND_E)); + + // Emit at least two trap instructions + EmitAndCountInstruction(MCInstBuilder(X86::INT3)); + EmitAndCountInstruction(MCInstBuilder(X86::INT3)); + + // Now .fill up to 0xe byte, so the ret happens on 0xf + MCSymbol *Dot = OutContext.createTempSymbol(); + OutStreamer->emitLabel(Dot); + const MCExpr *DotE = MCSymbolRefExpr::create(Dot, OutContext); + const MCExpr *BaseE = MCSymbolRefExpr::create( + TM.getSymbol(&MF->getFunction()), OutContext); + // .fill (0xf - ((DotE - BaseE) & 0xf)), 1, 0xcc + const MCExpr *FillE = MCBinaryExpr::createSub( + MCConstantExpr::create(0xf, OutContext), + MCBinaryExpr::createAnd( + MCBinaryExpr::createSub(DotE, BaseE, OutContext), + MCConstantExpr::create(0xf, OutContext), + OutContext), + OutContext); + OutStreamer->emitFill(*FillE, 0xCC); + + // And finally emit the jump target symbol + OutStreamer->emitLabel(RGSuccSym); + return; + } + + case X86::JMP_TRAP: { + MCSymbol *RGSuccSym = OutContext.createTempSymbol(); + const MCExpr *RGSuccExpr = MCSymbolRefExpr::create(RGSuccSym, OutContext); + EmitAndCountInstruction(MCInstBuilder(X86::JMP_1).addExpr(RGSuccExpr)); + EmitAndCountInstruction(MCInstBuilder(X86::INT3)); + EmitAndCountInstruction(MCInstBuilder(X86::INT3)); + OutStreamer->emitValueToAlignment(8, 0xCC, 1); + OutStreamer->emitLabel(RGSuccSym); + return; + } + case X86::SEH_PushReg: case X86::SEH_SaveReg: case X86::SEH_SaveXMM: diff --git a/llvm/lib/Target/X86/X86ReturnProtectorLowering.cpp b/llvm/lib/Target/X86/X86ReturnProtectorLowering.cpp new file mode 100644 index 000000000000..2eea9ac11fbc --- /dev/null +++ b/llvm/lib/Target/X86/X86ReturnProtectorLowering.cpp @@ -0,0 +1,121 @@ +//===-- X86ReturnProtectorLowering.cpp - ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the X86 implementation of ReturnProtectorLowering class. +// +//===----------------------------------------------------------------------===// + +#include "X86ReturnProtectorLowering.h" +#include "X86InstrBuilder.h" +#include "X86InstrInfo.h" +#include "X86MachineFunctionInfo.h" +#include "X86Subtarget.h" +#include "X86TargetMachine.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetOptions.h" +#include + +using namespace llvm; + +void X86ReturnProtectorLowering::insertReturnProtectorPrologue( + MachineFunction &MF, MachineBasicBlock &MBB, GlobalVariable *cookie) const { + + MachineBasicBlock::instr_iterator MI = MBB.instr_begin(); + DebugLoc MBBDL = MBB.findDebugLoc(MI); + const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + unsigned REG = MF.getFrameInfo().getReturnProtectorRegister(); + + BuildMI(MBB, MI, MBBDL, TII->get(X86::MOV64rm), REG) + .addReg(X86::RIP) + .addImm(0) + .addReg(0) + .addGlobalAddress(cookie) + .addReg(0); + addDirectMem(BuildMI(MBB, MI, MBBDL, TII->get(X86::XOR64rm), REG).addReg(REG), + X86::RSP); +} + +void X86ReturnProtectorLowering::insertReturnProtectorEpilogue( + MachineFunction &MF, MachineInstr &MI, GlobalVariable *cookie) const { + + MachineBasicBlock &MBB = *MI.getParent(); + DebugLoc MBBDL = MI.getDebugLoc(); + const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + unsigned REG = MF.getFrameInfo().getReturnProtectorRegister(); + + addDirectMem(BuildMI(MBB, MI, MBBDL, TII->get(X86::XOR64rm), REG).addReg(REG), + X86::RSP); + BuildMI(MBB, MI, MBBDL, TII->get(X86::CMP64rm)) + .addReg(REG) + .addReg(X86::RIP) + .addImm(0) + .addReg(0) + .addGlobalAddress(cookie) + .addReg(0); + BuildMI(MBB, MI, MBBDL, TII->get(X86::RETGUARD_JMP_TRAP)); +} + +bool X86ReturnProtectorLowering::opcodeIsReturn(unsigned opcode) const { + switch (opcode) { + case X86::RET: + case X86::RETL: + case X86::RETQ: + case X86::RETW: + case X86::RETIL: + case X86::RETIQ: + case X86::RETIW: + case X86::LRETL: + case X86::LRETQ: + case X86::LRETW: + case X86::LRETIL: + case X86::LRETIQ: + case X86::LRETIW: + return true; + default: + return false; + } +} + +void X86ReturnProtectorLowering::fillTempRegisters( + MachineFunction &MF, std::vector &TempRegs) const { + + TempRegs.push_back(X86::R11); + TempRegs.push_back(X86::R10); + const Function &F = MF.getFunction(); + if (!F.isVarArg()) { + // We can use any of the caller saved unused arg registers + switch (F.arg_size()) { + case 0: + TempRegs.push_back(X86::RDI); + LLVM_FALLTHROUGH; + case 1: + TempRegs.push_back(X86::RSI); + LLVM_FALLTHROUGH; + case 2: // RDX is the 2nd return register + case 3: + TempRegs.push_back(X86::RCX); + LLVM_FALLTHROUGH; + case 4: + TempRegs.push_back(X86::R8); + LLVM_FALLTHROUGH; + case 5: + TempRegs.push_back(X86::R9); + LLVM_FALLTHROUGH; + default: + break; + } + } +} diff --git a/llvm/lib/Target/X86/X86ReturnProtectorLowering.h b/llvm/lib/Target/X86/X86ReturnProtectorLowering.h new file mode 100644 index 000000000000..1eb5f123ce88 --- /dev/null +++ b/llvm/lib/Target/X86/X86ReturnProtectorLowering.h @@ -0,0 +1,45 @@ +//===-- X86ReturnProtectorLowering.h - ------------------------- -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the X86 implementation of ReturnProtectorLowering class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_X86_X86RETURNPROTECTORLOWERING_H +#define LLVM_LIB_TARGET_X86_X86RETURNPROTECTORLOWERING_H + +#include "llvm/CodeGen/ReturnProtectorLowering.h" + +namespace llvm { + +class X86ReturnProtectorLowering : public ReturnProtectorLowering { +public: + /// insertReturnProtectorPrologue/Epilogue - insert return protector + /// instrumentation in prologue or epilogue. + virtual void + insertReturnProtectorPrologue(MachineFunction &MF, MachineBasicBlock &MBB, + GlobalVariable *cookie) const override; + virtual void + insertReturnProtectorEpilogue(MachineFunction &MF, MachineInstr &MI, + GlobalVariable *cookie) const override; + + /// opcodeIsReturn - Reuturn true is the given opcode is a return + /// instruction needing return protection, false otherwise. + virtual bool opcodeIsReturn(unsigned opcode) const override; + + /// fillTempRegisters - Fill the list of available temp registers we can + /// use as a return protector register. + virtual void + fillTempRegisters(MachineFunction &MF, + std::vector &TempRegs) const override; +}; + +} // namespace llvm + +#endif diff --git a/llvm/lib/Target/X86/X86TargetMachine.cpp b/llvm/lib/Target/X86/X86TargetMachine.cpp index c8f76c210a3f..7e8dce56907f 100644 --- a/llvm/lib/Target/X86/X86TargetMachine.cpp +++ b/llvm/lib/Target/X86/X86TargetMachine.cpp @@ -537,6 +537,7 @@ void X86PassConfig::addPreEmitPass() { addPass(createX86EvexToVexInsts()); addPass(createX86DiscriminateMemOpsPass()); addPass(createX86InsertPrefetchPass()); + addPass(createX86FixupGadgetsPass()); addPass(createX86InsertX87waitPass()); } diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index 390925a03b73..28b0de96cd9a 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -969,6 +969,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::StackProtect: case Attribute::StackProtectReq: case Attribute::StackProtectStrong: + case Attribute::StackProtectGuard: case Attribute::StrictFP: case Attribute::UWTable: case Attribute::NoCfCheck: -- Gitee From 83d2b13f944dd3a009230a103ff4dbe0b3b4bf49 Mon Sep 17 00:00:00 2001 From: Hongjin Li Date: Thu, 4 Aug 2022 17:00:53 +0800 Subject: [PATCH 2/2] feature: ret-protector-guard add sspguard to IR bitcode Signed-off-by: Hongjin Li Change-Id: I18434ab0474bc9f3212a1cce9c65a1687e95e1a9 --- clang/include/clang/Basic/LangOptions.def | 2 +- llvm/lib/AsmParser/LLLexer.cpp | 1 + llvm/lib/AsmParser/LLParser.cpp | 4 ++++ llvm/lib/CodeGen/ReturnProtectorLowering.cpp | 3 +-- llvm/lib/IR/Attributes.cpp | 2 ++ llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp | 1 + llvm/utils/emacs/llvm-mode.el | 2 +- llvm/utils/kate/llvm.xml | 1 + llvm/utils/vim/syntax/llvm.vim | 1 + 9 files changed, 13 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index c01f0cca9c9c..02375ba9d0b7 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -324,7 +324,7 @@ ENUM_LANGOPT(ExternDeclNoDLLStorageClassVisibility, Visibility, 3, HiddenVisibil BENIGN_LANGOPT(SemanticInterposition , 1, 0, "semantic interposition") BENIGN_LANGOPT(HalfNoSemanticInterposition, 1, 0, "Like -fno-semantic-interposition but don't use local aliases") -ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff, +ENUM_LANGOPT(StackProtector, StackProtectorMode, 3, SSPOff, "stack protector mode") ENUM_LANGOPT(TrivialAutoVarInit, TrivialAutoVarInitKind, 2, TrivialAutoVarInitKind::Uninitialized, "trivial automatic variable initialization") diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 427de74f91ac..d28e7dde0ce7 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -685,6 +685,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(ssp); KEYWORD(sspreq); KEYWORD(sspstrong); + KEYWORD(sspguard); KEYWORD(strictfp); KEYWORD(safestack); KEYWORD(shadowcallstack); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 2a3fb8fb6658..93c60adbd8d8 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1386,6 +1386,8 @@ bool LLParser::parseFnAttributeValuePairs(AttrBuilder &B, case lltok::kw_sspreq: B.addAttribute(Attribute::StackProtectReq); break; case lltok::kw_sspstrong: B.addAttribute(Attribute::StackProtectStrong); break; + case lltok::kw_sspguard: + B.addAttribute(Attribute::StackProtectGuard); break; case lltok::kw_safestack: B.addAttribute(Attribute::SafeStack); break; case lltok::kw_shadowcallstack: B.addAttribute(Attribute::ShadowCallStack); break; @@ -1797,6 +1799,7 @@ bool LLParser::parseOptionalParamAttrs(AttrBuilder &B) { case lltok::kw_ssp: case lltok::kw_sspreq: case lltok::kw_sspstrong: + case lltok::kw_sspguard: case lltok::kw_safestack: case lltok::kw_shadowcallstack: case lltok::kw_strictfp: @@ -1906,6 +1909,7 @@ bool LLParser::parseOptionalReturnAttrs(AttrBuilder &B) { case lltok::kw_ssp: case lltok::kw_sspreq: case lltok::kw_sspstrong: + case lltok::kw_sspguard: case lltok::kw_safestack: case lltok::kw_shadowcallstack: case lltok::kw_strictfp: diff --git a/llvm/lib/CodeGen/ReturnProtectorLowering.cpp b/llvm/lib/CodeGen/ReturnProtectorLowering.cpp index f03e657a5384..98679aeca4fd 100644 --- a/llvm/lib/CodeGen/ReturnProtectorLowering.cpp +++ b/llvm/lib/CodeGen/ReturnProtectorLowering.cpp @@ -115,8 +115,7 @@ static bool hasAddressTaken(const Instruction *AI, /// setupReturnProtector - Checks the function for ROP friendly return /// instructions and sets ReturnProtectorNeeded if found. void ReturnProtectorLowering::setupReturnProtector(MachineFunction &MF) const { - if (MF.getFunction().hasFnAttribute("ret-protector") - || MF.getFunction().hasFnAttribute(Attribute::StackProtectGuard)) { + if (MF.getFunction().hasFnAttribute(Attribute::StackProtectGuard)) { for (auto &MBB : MF) { for (auto &T : MBB.terminators()) { if (opcodeIsReturn(T.getOpcode())) { diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index d4bfa680821f..09ce77d0e80a 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -435,6 +435,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const { return "sspreq"; if (hasAttribute(Attribute::StackProtectStrong)) return "sspstrong"; + if (hasAttribute(Attribute::StackProtectGuard)) + return "sspguard"; if (hasAttribute(Attribute::SafeStack)) return "safestack"; if (hasAttribute(Attribute::ShadowCallStack)) diff --git a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp index 1a8bb225a626..e7325fa849c6 100644 --- a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp @@ -71,6 +71,7 @@ static Attribute::AttrKind parseAttrKind(StringRef Kind) { .Case("ssp", Attribute::StackProtect) .Case("sspreq", Attribute::StackProtectReq) .Case("sspstrong", Attribute::StackProtectStrong) + .Case("sspguard", Attribute::StackProtectGuard) .Case("strictfp", Attribute::StrictFP) .Case("uwtable", Attribute::UWTable) .Default(Attribute::None); diff --git a/llvm/utils/emacs/llvm-mode.el b/llvm/utils/emacs/llvm-mode.el index bc24b12fb01f..46bfa3783a26 100644 --- a/llvm/utils/emacs/llvm-mode.el +++ b/llvm/utils/emacs/llvm-mode.el @@ -26,7 +26,7 @@ "inaccessiblemem_or_argmemonly" "inlinehint" "jumptable" "minsize" "mustprogress" "naked" "nobuiltin" "noduplicate" "nofree" "noimplicitfloat" "noinline" "nonlazybind" "noredzone" "noreturn" "norecurse" "noundef" "nounwind" "optnone" "optsize" "readnone" "readonly" "returns_twice" - "speculatable" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag" + "speculatable" "ssp" "sspreq" "sspstrong" "sspguard" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag" "sanitize_thread" "sanitize_memory" "strictfp" "uwtable" "willreturn" "writeonly" "immarg") 'symbols) . font-lock-constant-face) ;; Variables '("%[-a-zA-Z$._][-a-zA-Z$._0-9]*" . font-lock-variable-name-face) diff --git a/llvm/utils/kate/llvm.xml b/llvm/utils/kate/llvm.xml index 5132aaf6fba5..6355cf0edd62 100644 --- a/llvm/utils/kate/llvm.xml +++ b/llvm/utils/kate/llvm.xml @@ -96,6 +96,7 @@ ssp sspreq sspstrong + sspguard addrspace diff --git a/llvm/utils/vim/syntax/llvm.vim b/llvm/utils/vim/syntax/llvm.vim index 0914f6de3829..4c722d503b10 100644 --- a/llvm/utils/vim/syntax/llvm.vim +++ b/llvm/utils/vim/syntax/llvm.vim @@ -151,6 +151,7 @@ syn keyword llvmKeyword \ ssp \ sspreq \ sspstrong + \ sspguard \ strictfp \ swiftcc \ swiftself -- Gitee