Merge in a diff from base: Improve the X86FixupGadgets pass
Merge in a bug fix from upstream: [MC] Make symbol version errors non-fatal Index: Makefile =================================================================== RCS file: /home/cvs/ports/devel/llvm/Makefile,v retrieving revision 1.213 diff -u -p -u -p -r1.213 Makefile --- Makefile 20 Feb 2019 00:24:11 -0000 1.213 +++ Makefile 22 Feb 2019 14:33:44 -0000 @@ -20,7 +20,7 @@ PKGSPEC-main = llvm-=${LLVM_V} PKGNAME-main = llvm-${LLVM_V} PKGNAME-python = py-llvm-${LLVM_V} PKGNAME-lldb = lldb-${LLVM_V} -REVISION-main = 6 +REVISION-main = 7 REVISION-lldb = 0 CATEGORIES = devel DISTFILES = llvm-${LLVM_V}.src${EXTRACT_SUFX} \ Index: patches/patch-lib_MC_ELFObjectWriter_cpp =================================================================== RCS file: patches/patch-lib_MC_ELFObjectWriter_cpp diff -N patches/patch-lib_MC_ELFObjectWriter_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-lib_MC_ELFObjectWriter_cpp 22 Feb 2019 14:36:33 -0000 @@ -0,0 +1,34 @@ +$OpenBSD$ + +[MC] Make symbol version errors non-fatal + +Index: lib/MC/ELFObjectWriter.cpp +--- lib/MC/ELFObjectWriter.cpp.orig ++++ lib/MC/ELFObjectWriter.cpp +@@ -1258,14 +1258,20 @@ void ELFObjectWriter::executePostLayoutBinding(MCAssem + if (!Symbol.isUndefined() && !Rest.startswith("@@@")) + continue; + +- // FIXME: produce a better error message. ++ // FIXME: Get source locations for these errors or diagnose them earlier. + if (Symbol.isUndefined() && Rest.startswith("@@") && +- !Rest.startswith("@@@")) +- report_fatal_error("A @@ version cannot be undefined"); ++ !Rest.startswith("@@@")) { ++ Asm.getContext().reportError(SMLoc(), "versioned symbol " + AliasName + ++ " must be defined"); ++ continue; ++ } + +- if (Renames.count(&Symbol) && Renames[&Symbol] != Alias) +- report_fatal_error(llvm::Twine("Multiple symbol versions defined for ") + +- Symbol.getName()); ++ if (Renames.count(&Symbol) && Renames[&Symbol] != Alias) { ++ Asm.getContext().reportError( ++ SMLoc(), llvm::Twine("multiple symbol versions defined for ") + ++ Symbol.getName()); ++ continue; ++ } + + Renames.insert(std::make_pair(&Symbol, Alias)); + } Index: patches/patch-lib_Target_X86_X86FixupGadgets_cpp =================================================================== RCS file: /home/cvs/ports/devel/llvm/patches/patch-lib_Target_X86_X86FixupGadgets_cpp,v retrieving revision 1.2 diff -u -p -u -p -r1.2 patch-lib_Target_X86_X86FixupGadgets_cpp --- patches/patch-lib_Target_X86_X86FixupGadgets_cpp 28 Jan 2019 06:27:28 -0000 1.2 +++ patches/patch-lib_Target_X86_X86FixupGadgets_cpp 22 Feb 2019 19:20:40 -0000 @@ -1,15 +1,16 @@ $OpenBSD: patch-lib_Target_X86_X86FixupGadgets_cpp,v 1.2 2019/01/28 06:27:28 jca Exp $ -Add a clang pass that identifies potential ROP gadgets and replaces ROP -friendly instructions with safe alternatives. This initial commit fixes -3 instruction forms that will lower to include a c3 (return) byte. -Additional problematic instructions can be fixed incrementally using -this framework. +- Add a clang pass that identifies potential ROP gadgets and replaces ROP + friendly instructions with safe alternatives. This initial commit fixes + 3 instruction forms that will lower to include a c3 (return) byte. + Additional problematic instructions can be fixed incrementally using + this framework. +- Improve the X86FixupGadgets pass Index: lib/Target/X86/X86FixupGadgets.cpp --- lib/Target/X86/X86FixupGadgets.cpp.orig +++ lib/Target/X86/X86FixupGadgets.cpp -@@ -0,0 +1,273 @@ +@@ -0,0 +1,720 @@ +//===-- X86FixupGadgets.cpp - Fixup Instructions that make ROP Gadgets ----===// +// +// The LLVM Compiler Infrastructure @@ -47,10 +48,10 @@ Index: lib/Target/X86/X86FixupGadgets.cp + +#define DEBUG_TYPE FIXUPGADGETS_NAME + -+// Toggle with cc1 option: -backend-option -x86-fixup-gadgets=<true|false> ++// Toggle with cc1 option: -mllvm -x86-fixup-gadgets=<true|false> +static cl::opt<bool> FixupGadgets( -+ "x86-fixup-gadgets", -+ cl::desc("Replace ROP friendly instructions with alternatives"), ++ "x86-fixup-gadgets", cl::Hidden, ++ cl::desc("Replace ROP friendly instructions with safe alternatives"), + cl::init(true)); + +namespace { @@ -78,24 +79,33 @@ Index: lib/Target/X86/X86FixupGadgets.cp + const X86InstrInfo *TII; + const X86RegisterInfo *TRI; + bool Is64Bit; -+ enum InstrType { -+ NoType = 0, -+ OneGPRegC3, -+ TwoGPRegC3, -+ ThreeGPRegC3, -+ }; + -+ /// If an Instr has a ROP friendly construct, return it -+ InstrType isROPFriendly(MachineInstr &MI) const; ++ struct FixupInfo { ++ unsigned op1; ++ unsigned op2; ++ bool fixup; ++ bool align; ++ }; + -+ /// Helper functions for various kinds of instructions -+ bool isOneGPRegC3(MachineInstr &MI) const; -+ bool isTwoGPRegC3(MachineInstr &MI) const; -+ bool isThreeGPRegC3(MachineInstr &MI) const; ++ 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; + -+ /// Replace ROP friendly instructions with safe alternatives + bool fixupInstruction(MachineFunction &MF, MachineBasicBlock &MBB, -+ MachineInstr &MI, InstrType type); ++ MachineInstr &MI, struct FixupInfo Info); +}; +char FixupGadgetsPass::ID = 0; +} // namespace @@ -104,155 +114,593 @@ Index: lib/Target/X86/X86FixupGadgets.cp + return new FixupGadgetsPass(); +} + -+bool FixupGadgetsPass::isOneGPRegC3(MachineInstr &MI) const { -+ MachineOperand &MO = MI.getOperand(0); -+ return MO.isReg() && MO.getReg() == X86::EBX && -+ (MI.getDesc().TSFlags & X86II::FormMask) == X86II::MRMDestReg; ++uint8_t FixupGadgetsPass::getRegNum(const MachineOperand &MO) const { ++ return TRI->getEncodingValue(MO.getReg()) & 0x7; +} + -+bool FixupGadgetsPass::isTwoGPRegC3(MachineInstr &MI) const { -+ bool DestSet = false; -+ bool SrcSet = false; -+ bool OpcodeSet = false; -+ MachineOperand &MO0 = MI.getOperand(0); -+ MachineOperand &MO1 = MI.getOperand(1); -+ if (!(MO0.isReg() && MO1.isReg())) -+ return false; ++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; ++} + -+ unsigned dstReg = MO0.getReg(); -+ if (dstReg == X86::RBX || dstReg == X86::EBX || dstReg == X86::BX || -+ dstReg == X86::BL) -+ DestSet = true; ++bool FixupGadgetsPass::isROPFriendlyRegPair(const MachineOperand &Dst, ++ const MachineOperand &Src) const { + -+ if (!DestSet) -+ return false; ++ if (!Dst.isReg() || !Src.isReg()) ++ llvm_unreachable("Testing non registers for bad reg pair!"); + -+ unsigned srcReg = MO1.getReg(); -+ if (srcReg == X86::RAX || srcReg == X86::EAX || srcReg == X86::AX || -+ srcReg == X86::AL) -+ SrcSet = true; ++ uint8_t Mod = 3; ++ uint8_t RegOpcode = getRegNum(Src); ++ uint8_t RM = getRegNum(Dst); ++ return badModRM(Mod, RegOpcode, RM); ++} + -+ if (!SrcSet) -+ return false; ++bool FixupGadgetsPass::isROPFriendlyReg(const MachineOperand &Dst, uint8_t RegOpcode) const { + -+ if ((MI.getDesc().TSFlags & X86II::FormMask) == X86II::MRMDestReg) -+ OpcodeSet = true; ++ if (!Dst.isReg()) ++ llvm_unreachable("Testing non register for bad reg!"); + -+ return DestSet && SrcSet && OpcodeSet; ++ uint8_t Mod = 3; ++ uint8_t RM = getRegNum(Dst); ++ return badModRM(Mod, RegOpcode, RM); +} + -+bool FixupGadgetsPass::isThreeGPRegC3(MachineInstr &MI) const { -+ bool DestSet = false; -+ bool SrcSet = false; -+ bool OpcodeSet = false; -+ -+ MachineOperand &MO0 = MI.getOperand(0); -+ MachineOperand &MO1 = MI.getOperand(1); -+ MachineOperand &MO2 = MI.getOperand(2); -+ if (!(MO0.isReg() && MO1.isReg() && MO2.isReg() && -+ MO0.getReg() == MO1.getReg())) -+ return false; ++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; ++} + -+ unsigned dstReg = MO0.getReg(); -+ if (dstReg == X86::RBX || dstReg == X86::EBX || dstReg == X86::BX || -+ dstReg == X86::BL) -+ DestSet = true; ++void FixupGadgetsPass::checkSIB(const MachineInstr &MI, unsigned CurOp, ++ struct FixupInfo &info) const { + -+ if (!DestSet) -+ return false; ++ 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; ++ } ++} + -+ unsigned srcReg = MO2.getReg(); -+ if (srcReg == X86::RAX || srcReg == X86::EAX || srcReg == X86::AX || -+ srcReg == X86::AL) -+ SrcSet = true; ++struct FixupGadgetsPass::FixupInfo ++FixupGadgetsPass::isROPFriendly(MachineInstr &MI) const { + -+ if (!SrcSet) -+ return false; ++ 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; ++ } ++ } + -+ if ((MI.getDesc().TSFlags & X86II::FormMask) == X86II::MRMDestReg) -+ OpcodeSet = true; ++ 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::ACQUIRE_MOV8rm: ++ case X86::ACQUIRE_MOV16rm: ++ case X86::ACQUIRE_MOV32rm: ++ case X86::ACQUIRE_MOV64rm: ++ goto Handle_MRMSrcMem; ++ case X86::RELEASE_MOV8mr: ++ case X86::RELEASE_MOV16mr: ++ case X86::RELEASE_MOV32mr: ++ case X86::RELEASE_MOV64mr: ++ case X86::RELEASE_ADD8mr: ++ case X86::RELEASE_ADD32mr: ++ case X86::RELEASE_ADD64mr: ++ case X86::RELEASE_AND8mr: ++ case X86::RELEASE_AND32mr: ++ case X86::RELEASE_AND64mr: ++ case X86::RELEASE_OR8mr: ++ case X86::RELEASE_OR32mr: ++ case X86::RELEASE_OR64mr: ++ case X86::RELEASE_XOR8mr: ++ case X86::RELEASE_XOR32mr: ++ case X86::RELEASE_XOR64mr: ++ goto Handle_MRMDestMem; ++ case X86::RELEASE_MOV8mi: ++ case X86::RELEASE_MOV16mi: ++ case X86::RELEASE_MOV32mi: ++ case X86::RELEASE_ADD8mi: ++ case X86::RELEASE_MOV64mi32: ++ case X86::RELEASE_ADD32mi: ++ case X86::RELEASE_ADD64mi32: ++ case X86::RELEASE_AND8mi: ++ case X86::RELEASE_AND32mi: ++ case X86::RELEASE_AND64mi32: ++ case X86::RELEASE_OR8mi: ++ case X86::RELEASE_OR32mi: ++ case X86::RELEASE_OR64mi32: ++ case X86::RELEASE_XOR8mi: ++ case X86::RELEASE_XOR32mi: ++ case X86::RELEASE_XOR64mi32: ++ case X86::RELEASE_INC8m: ++ case X86::RELEASE_INC16m: ++ case X86::RELEASE_INC32m: ++ case X86::RELEASE_INC64m: ++ case X86::RELEASE_DEC8m: ++ case X86::RELEASE_DEC16m: ++ case X86::RELEASE_DEC32m: ++ case X86::RELEASE_DEC64m: ++ goto Handle_MRMXm; ++ 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: { ++ Handle_MRMDestMem: ++ checkSIB(MI, CurOp, info); ++ unsigned opcode = MI.getOpcode(); ++ if (opcode == X86::MOVNTImr || opcode == X86::MOVNTI_64mr) ++ info.align = true; ++ break; ++ } ++ case X86II::MRMSrcMem: { ++ Handle_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: { ++ Handle_MRMXm: ++ 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; ++} + -+ return DestSet && SrcSet && OpcodeSet; ++bool FixupGadgetsPass::needsFixup(struct FixupInfo &fi) const { ++ return (fi.fixup == true); +} + -+FixupGadgetsPass::InstrType -+FixupGadgetsPass::isROPFriendly(MachineInstr &MI) const { -+ switch (MI.getNumExplicitOperands()) { -+ case 1: -+ return isOneGPRegC3(MI) ? OneGPRegC3 : NoType; -+ case 2: -+ return isTwoGPRegC3(MI) ? TwoGPRegC3 : NoType; -+ case 3: -+ return isThreeGPRegC3(MI) ? ThreeGPRegC3 : NoType; ++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!"); + } -+ return NoType; ++} ++ ++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::fixupInstruction(MachineFunction &MF, + MachineBasicBlock &MBB, -+ MachineInstr &MI, InstrType type) { ++ MachineInstr &MI, FixupInfo Info) { + -+ if (type == NoType) ++ if (!needsAlign(Info) && !needsFixup(Info)) + return false; + -+ MachineOperand *MO0, *MO1, *MO2; + 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 SREG = Is64Bit ? X86::RAX : X86::EAX; -+ unsigned DREG = Is64Bit ? X86::RBX : X86::EBX; -+ unsigned tmpReg; ++ ++ 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; ++ } + + // Swap the two registers to start + BuildMI(MBB, MI, DL, TII->get(XCHG)) -+ .addReg(DREG, RegState::Define) -+ .addReg(SREG, RegState::Define) -+ .addReg(DREG).addReg(SREG); -+ -+ switch (type) { -+ case OneGPRegC3: -+ MO0 = &MI.getOperand(0); -+ switch (MO0->getReg()) { -+ case X86::RBX: -+ tmpReg = X86::RAX; -+ break; -+ case X86::EBX: -+ tmpReg = X86::EAX; -+ break; -+ case X86::BX: -+ tmpReg = X86::AX; -+ break; -+ case X86::BL: -+ tmpReg = X86::AL; -+ break; -+ default: -+ llvm_unreachable("Unknown DestReg in OneGPRegC3 fixup"); -+ } -+ BuildMI(MBB, MI, DL, MI.getDesc(), tmpReg); -+ break; -+ case TwoGPRegC3: -+ MO0 = &MI.getOperand(0); -+ MO1 = &MI.getOperand(1); -+ BuildMI(MBB, MI, DL, MI.getDesc(), MO1->getReg()).addReg(MO0->getReg()); -+ break; -+ case ThreeGPRegC3: -+ // Swap args around and set new dest reg -+ MO0 = &MI.getOperand(0); // Destination -+ MO2 = &MI.getOperand(2); // Source 2 == Other -+ BuildMI(MBB, MI, DL, MI.getDesc(), MO2->getReg()) -+ .addReg(MO2->getReg()) -+ .addReg(MO0->getReg()); -+ break; -+ default: -+ llvm_unreachable("Unknown FixupGadgets Instruction Type"); ++ .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 them back to finish -+ BuildMI(MBB, MI, DL, TII->get(XCHG)) -+ .addReg(DREG, RegState::Define) -+ .addReg(SREG, RegState::Define) -+ .addReg(DREG).addReg(SREG); -+ // Erase original instruction -+ MI.eraseFromParent(); ++ // 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; +} @@ -265,17 +713,17 @@ Index: lib/Target/X86/X86FixupGadgets.cp + TII = STI->getInstrInfo(); + TRI = STI->getRegisterInfo(); + Is64Bit = STI->is64Bit(); -+ std::vector<std::pair<MachineInstr *, InstrType>> fixups; -+ InstrType type; ++ std::vector<std::pair<MachineInstr *, FixupInfo>> fixups; ++ FixupInfo info; + + bool modified = false; + + for (auto &MBB : MF) { + fixups.clear(); + for (auto &MI : MBB) { -+ type = isROPFriendly(MI); -+ if (type != NoType) -+ fixups.push_back(std::make_pair(&MI, type)); ++ 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); Index: patches/patch-lib_Target_X86_X86InstrCompiler_td =================================================================== RCS file: /home/cvs/ports/devel/llvm/patches/patch-lib_Target_X86_X86InstrCompiler_td,v retrieving revision 1.1 diff -u -p -u -p -r1.1 patch-lib_Target_X86_X86InstrCompiler_td --- patches/patch-lib_Target_X86_X86InstrCompiler_td 6 Jul 2018 06:55:10 -0000 1.1 +++ patches/patch-lib_Target_X86_X86InstrCompiler_td 22 Feb 2019 19:22:37 -0000 @@ -1,27 +1,28 @@ $OpenBSD: patch-lib_Target_X86_X86InstrCompiler_td,v 1.1 2018/07/06 06:55:10 ajacoutot Exp $ -Add RETGUARD to clang for amd64. This security mechanism uses per-function -random cookies to protect access to function return instructions, with the -effect that the integrity of the return address is protected, and function -return instructions are harder to use in ROP gadgets. - -On function entry the return address is combined with a per-function random -cookie and stored in the stack frame. The integrity of this value is verified -before function return, and if this check fails, the program aborts. In this way -RETGUARD is an improved stack protector, since the cookies are per-function. The -verification routine is constructed such that the binary space immediately -before each ret instruction is padded with int03 instructions, which makes these -return instructions difficult to use in ROP gadgets. In the kernel, this has the -effect of removing approximately 50% of total ROP gadgets, and 15% of unique -ROP gadgets compared to the 6.3 release kernel. Function epilogues are -essentially gadget free, leaving only the polymorphic gadgets that result from -jumping into the instruction stream partway through other instructions. Work to -remove these gadgets will continue through other mechanisms. +- Add RETGUARD to clang for amd64. This security mechanism uses per-function + random cookies to protect access to function return instructions, with the + effect that the integrity of the return address is protected, and function + return instructions are harder to use in ROP gadgets. + + On function entry the return address is combined with a per-function random + cookie and stored in the stack frame. The integrity of this value is verified + before function return, and if this check fails, the program aborts. In this way + RETGUARD is an improved stack protector, since the cookies are per-function. The + verification routine is constructed such that the binary space immediately + before each ret instruction is padded with int03 instructions, which makes these + return instructions difficult to use in ROP gadgets. In the kernel, this has the + effect of removing approximately 50% of total ROP gadgets, and 15% of unique + ROP gadgets compared to the 6.3 release kernel. Function epilogues are + essentially gadget free, leaving only the polymorphic gadgets that result from + jumping into the instruction stream partway through other instructions. Work to + remove these gadgets will continue through other mechanisms. +- Improve the X86FixupGadgets pass Index: lib/Target/X86/X86InstrCompiler.td --- lib/Target/X86/X86InstrCompiler.td.orig +++ lib/Target/X86/X86InstrCompiler.td -@@ -267,6 +267,21 @@ def MORESTACK_RET_RESTORE_R10 : I<0, Pseudo, (outs), ( +@@ -267,6 +267,25 @@ def MORESTACK_RET_RESTORE_R10 : I<0, Pseudo, (outs), ( } //===----------------------------------------------------------------------===// @@ -37,6 +38,10 @@ Index: lib/Target/X86/X86InstrCompiler.t +// the return value (ie mov %ecx, %eax instead of mov %cl, %al). +let isCodeGenOnly = 1, Uses = [EFLAGS] in { +def RETGUARD_JMP_TRAP: I<0, Pseudo, (outs), (ins), "", []>; ++} ++ ++let isCodeGenOnly = 1 in { ++def JMP_TRAP: I<0, Pseudo, (outs), (ins), "", []>; +} + +//===----------------------------------------------------------------------===// Index: patches/patch-lib_Target_X86_X86MCInstLower_cpp =================================================================== RCS file: /home/cvs/ports/devel/llvm/patches/patch-lib_Target_X86_X86MCInstLower_cpp,v retrieving revision 1.5 diff -u -p -u -p -r1.5 patch-lib_Target_X86_X86MCInstLower_cpp --- patches/patch-lib_Target_X86_X86MCInstLower_cpp 28 Jan 2019 06:27:28 -0000 1.5 +++ patches/patch-lib_Target_X86_X86MCInstLower_cpp 22 Feb 2019 19:12:52 -0000 @@ -22,7 +22,7 @@ $OpenBSD: patch-lib_Target_X86_X86MCInst Index: lib/Target/X86/X86MCInstLower.cpp --- lib/Target/X86/X86MCInstLower.cpp.orig +++ lib/Target/X86/X86MCInstLower.cpp -@@ -1831,6 +1831,16 @@ void X86AsmPrinter::EmitInstruction(const MachineInstr +@@ -1831,6 +1831,27 @@ void X86AsmPrinter::EmitInstruction(const MachineInstr MCInstBuilder(X86::MOV64rr).addReg(X86::R10).addReg(X86::RAX)); return; @@ -36,10 +36,21 @@ Index: lib/Target/X86/X86MCInstLower.cpp + 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: -@@ -2257,4 +2267,10 @@ void X86AsmPrinter::EmitInstruction(const MachineInstr +@@ -2257,4 +2278,10 @@ void X86AsmPrinter::EmitInstruction(const MachineInstr } EmitAndCountInstruction(TmpInst); Index: patches/patch-tools_clang_include_clang_Driver_Options_td =================================================================== RCS file: /home/cvs/ports/devel/llvm/patches/patch-tools_clang_include_clang_Driver_Options_td,v retrieving revision 1.17 diff -u -p -u -p -r1.17 patch-tools_clang_include_clang_Driver_Options_td --- patches/patch-tools_clang_include_clang_Driver_Options_td 11 Feb 2019 05:24:16 -0000 1.17 +++ patches/patch-tools_clang_include_clang_Driver_Options_td 22 Feb 2019 19:24:34 -0000 @@ -1,13 +1,14 @@ $OpenBSD: patch-tools_clang_include_clang_Driver_Options_td,v 1.17 2019/02/11 05:24:16 jca Exp $ - Add ret protctor options as no-ops. +- Improve the X86FixupGadgets pass - Alias the command line parameter -p to -pg. - implement -msave-args in clang/llvm, like the sun did for gcc Index: tools/clang/include/clang/Driver/Options.td --- tools/clang/include/clang/Driver/Options.td.orig +++ tools/clang/include/clang/Driver/Options.td -@@ -1595,6 +1595,10 @@ def fstack_protector_strong : Flag<["-"], "fstack-prot +@@ -1595,6 +1595,14 @@ def fstack_protector_strong : Flag<["-"], "fstack-prot HelpText<"Use a strong heuristic to apply stack protectors to functions">; def fstack_protector : Flag<["-"], "fstack-protector">, Group<f_Group>, HelpText<"Enable stack protectors for functions potentially vulnerable to stack smashing">; @@ -15,10 +16,14 @@ Index: tools/clang/include/clang/Driver/ + HelpText<"Disable return protector">; +def fret_protector : Flag<["-"], "fret-protector">, Group<f_Group>, + HelpText<"Enable return protector">; ++def fno_fixup_gadgets : Flag<["-"], "fno-fixup-gadgets">, Group<f_Group>, ++ HelpText<"Disable FixupGadgets pass (x86 only)">; ++def ffixup_gadgets : Flag<["-"], "ffixup-gadgets">, Group<f_Group>, ++ HelpText<"Replace ROP friendly instructions with safe alternatives (x86 only)">; def fstandalone_debug : Flag<["-"], "fstandalone-debug">, Group<f_Group>, Flags<[CoreOption]>, HelpText<"Emit full debug info for all types used by the program">; def fno_standalone_debug : Flag<["-"], "fno-standalone-debug">, Group<f_Group>, Flags<[CoreOption]>, -@@ -2359,7 +2363,7 @@ def pthreads : Flag<["-"], "pthreads">; +@@ -2359,7 +2367,7 @@ def pthreads : Flag<["-"], "pthreads">; def pthread : Flag<["-"], "pthread">, Flags<[CC1Option]>, HelpText<"Support POSIX threads in generated code">; def no_pthread : Flag<["-"], "no-pthread">, Flags<[CC1Option]>; @@ -27,7 +32,7 @@ Index: tools/clang/include/clang/Driver/ def pie : Flag<["-"], "pie">; def read__only__relocs : Separate<["-"], "read_only_relocs">; def remap : Flag<["-"], "remap">; -@@ -2810,6 +2814,8 @@ def mretpoline : Flag<["-"], "mretpoline">, Group<m_x8 +@@ -2810,6 +2818,8 @@ def mretpoline : Flag<["-"], "mretpoline">, Group<m_x8 def mno_retpoline : Flag<["-"], "mno-retpoline">, Group<m_x86_Features_Group>; def mretpoline_external_thunk : Flag<["-"], "mretpoline-external-thunk">, Group<m_x86_Features_Group>; def mno_retpoline_external_thunk : Flag<["-"], "mno-retpoline-external-thunk">, Group<m_x86_Features_Group>; Index: patches/patch-tools_clang_lib_Driver_ToolChains_Clang_cpp =================================================================== RCS file: /home/cvs/ports/devel/llvm/patches/patch-tools_clang_lib_Driver_ToolChains_Clang_cpp,v retrieving revision 1.7 diff -u -p -u -p -r1.7 patch-tools_clang_lib_Driver_ToolChains_Clang_cpp --- patches/patch-tools_clang_lib_Driver_ToolChains_Clang_cpp 28 Jan 2019 06:27:28 -0000 1.7 +++ patches/patch-tools_clang_lib_Driver_ToolChains_Clang_cpp 22 Feb 2019 19:28:40 -0000 @@ -22,6 +22,7 @@ $OpenBSD: patch-tools_clang_lib_Driver_T jumping into the instruction stream partway through other instructions. Work to remove these gadgets will continue through other mechanisms. - Add retguard for arm64. +- Improve the X86FixupGadgets pass - On OpenBSD disable the malloc/calloc/realloc/free/str*dup builtins, since they can perform strange transforms and optimizations. Some of those could gain a slight advantage, but would avoid the variety of important runtime @@ -65,7 +66,7 @@ Index: tools/clang/lib/Driver/ToolChains if (Arg *A = Args.getLastArg(options::OPT_freroll_loops, options::OPT_fno_reroll_loops)) -@@ -4101,6 +4105,24 @@ void Clang::ConstructJob(Compilation &C, const JobActi +@@ -4101,6 +4105,34 @@ void Clang::ConstructJob(Compilation &C, const JobActi RenderSSPOptions(getToolChain(), Args, CmdArgs, KernelOrKext); @@ -87,10 +88,20 @@ Index: tools/clang/lib/Driver/ToolChains + CmdArgs.push_back(Args.MakeArgString(Twine("-ret-protector"))); + } + ++ // -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"))); ++ } ++ // Translate -mstackrealign if (Args.hasFlag(options::OPT_mstackrealign, options::OPT_mno_stackrealign, false)) -@@ -4579,6 +4601,18 @@ void Clang::ConstructJob(Compilation &C, const JobActi +@@ -4579,6 +4611,18 @@ void Clang::ConstructJob(Compilation &C, const JobActi llvm::sys::path::replace_extension(F, "opt.yaml"); CmdArgs.push_back(Args.MakeArgString(F)); }