diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2024-06-02 15:42:20 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2024-06-02 15:42:20 +0000 |
commit | 07a6ad13daad21c466066b4eb50780ac190f6d77 (patch) | |
tree | 61182688e5b0b17fae32a0ef9ca8b928b7cc05b1 /gnu | |
parent | f7a83c2a5f643ae914fbb3e474894959d8a38e1e (diff) |
add -fret-clean option (amd64 and i386 only at first), defaulting to off.
This causes the caller to cleans the return address off the stack after
a callq completes. The option is best used in low-level libraries (such as
libc), because libc contains low-level system call stubs. The option
reduces hints (found on the stale parts of the stack) about libc.so's mapping
location, and together with random-relinking, relro got/pic, and xonly
makes some exploit methods more difficult.
ok mortimer, mlarkin, much discussion with kettenis, in snaps for 2 weeks.
Diffstat (limited to 'gnu')
-rw-r--r-- | gnu/llvm/llvm/lib/Target/X86/X86RetClean.cpp | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/gnu/llvm/llvm/lib/Target/X86/X86RetClean.cpp b/gnu/llvm/llvm/lib/Target/X86/X86RetClean.cpp new file mode 100644 index 00000000000..623bfede522 --- /dev/null +++ b/gnu/llvm/llvm/lib/Target/X86/X86RetClean.cpp @@ -0,0 +1,115 @@ +//===-- X86RetClean.cpp - Clean Retaddr off stack upon function return ----===// +// +// 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 clears the ret-address from +/// the top of the stack, immediately upon return to the caller, the goal +/// is remove this subtle but powerful info-leak which hints at the +/// address space location of the lower level libraries. +/// +//===----------------------------------------------------------------------===// + +#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 RETCLEAN_DESC "X86 Ret Clean" +#define RETCLEAN_NAME "x86-ret-clean" + +#define DEBUG_TYPE RETCLEAN_NAME + +// Toggle with cc1 option: -mllvm -x86-ret-clean=<true|false> +static cl::opt<bool> RetClean( + "x86-ret-clean", cl::Hidden, + cl::desc("clean return address off stack after call"), + cl::init(false)); + +namespace { +class RetCleanPass : public MachineFunctionPass { + +public: + static char ID; + + StringRef getPassName() const override { return RETCLEAN_DESC; } + + RetCleanPass() + : MachineFunctionPass(ID) {} + + /// Loop over all the instructions and replace ret with ret+clean + bool runOnMachineFunction(MachineFunction &MF) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + +private: + bool fixupInstruction(MachineFunction &MF, MachineBasicBlock &MBB, + MachineInstr &MI); +}; +char RetCleanPass::ID = 0; +} // namespace + +FunctionPass *llvm::createX86RetCleanPass() { + return new RetCleanPass(); +} + +bool RetCleanPass::fixupInstruction(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineInstr &MI) { + + const X86InstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo(); + bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64; + unsigned Opc = Is64Bit ? X86::MOV64mi32 : X86::MOV32mi; + unsigned Offset = Is64Bit ? -8 : -4; + Register SPReg = Is64Bit ? X86::RSP : X86::ESP; + + // add "movq $0, -8(%rsp)" (or similar) in caller, to clear the + // ret-addr info-leak off the stack + addRegOffset(BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(Opc)), + SPReg, false, Offset) + .addImm(0); + return true; +} + +bool RetCleanPass::runOnMachineFunction(MachineFunction &MF) { + if (!RetClean) + return false; + + bool modified = false; + + for (auto &MBB : MF) { + std::vector<MachineInstr*> fixups; + bool foundcall = false; + + for (auto &MI : MBB) { + if (MI.isCall()) { + foundcall = true; // queue the insert before the next MI + } else if (foundcall) { + fixups.push_back(&MI); + foundcall = false; + } + } + for (auto *fixup : fixups) + modified |= fixupInstruction(MF, MBB, *fixup); + } + return modified; +} |