llvm.org GIT mirror llvm / 21befa7
Insert random noops to increase security against ROP attacks (llvm) A pass that adds random noops to X86 binaries to introduce diversity with the goal of increasing security against most return-oriented programming attacks. Command line options: -noop-insertion // Enable noop insertion. -noop-insertion-percentage=X // X% of assembly instructions will have a noop prepended (default: 50%, requires -noop-insertion) -max-noops-per-instruction=X // Randomly generate X noops per instruction. ie. roll the dice X times with probability set above (default: 1). This doesn't guarantee X noop instructions. In addition, the following 'quick switch' in clang enables basic diversity using default settings (currently: noop insertion and schedule randomization; it is intended to be extended in the future). -fdiversify This is the llvm part of the patch. clang part: D3393 http://reviews.llvm.org/D3392 Patch by Stephen Crane (@rinon) git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@225908 91177308-0d34-0410-b5e6-96231b3b80d8 JF Bastien 4 years ago
17 changed file(s) with 428 addition(s) and 4 deletion(s). Raw diff Collapse all Expand all
205205 FunctionSections("function-sections",
206206 cl::desc("Emit functions into separate sections"),
207207 cl::init(false));
208
209 cl::opt
210 NoopInsertion("noop-insertion",
211 cl::desc("Randomly add Noop instructions to create fine-grained "
212 "code layout diversity."),
213 cl::init(false));
208214
209215 cl::opt
210216 JTableType("jump-table-type",
283289 Options.UseInitArray = !UseCtors;
284290 Options.DataSections = DataSections;
285291 Options.FunctionSections = FunctionSections;
292 Options.NoopInsertion = NoopInsertion;
286293
287294 Options.MCOptions = InitMCTargetOptionsFromFlags();
288295 Options.JTType = JTableType;
0 //===-- NoopInsertion.h - Noop Insertion ------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This pass adds fine-grained diversity by displacing code using randomly
10 // placed (optionally target supplied) Noop instructions.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_CODEGEN_NOOPINSERTION_H
15 #define LLVM_CODEGEN_NOOPINSERTION_H
16
17 #include "llvm/CodeGen/MachineFunctionPass.h"
18 #include
19
20 namespace llvm {
21
22 class RandomNumberGenerator;
23
24 class NoopInsertion : public MachineFunctionPass {
25 public:
26 static char ID;
27
28 NoopInsertion();
29
30 private:
31 bool runOnMachineFunction(MachineFunction &MF) override;
32
33 void getAnalysisUsage(AnalysisUsage &AU) const override;
34
35 std::unique_ptr RNG;
36
37 // Uniform real distribution from 0 to 100
38 std::uniform_real_distribution Distribution =
39 std::uniform_real_distribution(0, 100);
40 };
41 }
42
43 #endif // LLVM_CODEGEN_NOOPINSERTION_H
602602 /// the intrinsic for later emission to the StackMap.
603603 extern char &StackMapLivenessID;
604604
605 /// NoopInsertion - This pass adds fine-grained diversity by displacing code
606 /// using randomly placed (optionally target supplied) Noop instructions.
607 extern char &NoopInsertionID;
608
605609 /// createJumpInstrTables - This pass creates jump-instruction tables.
606610 ModulePass *createJumpInstrTablesPass();
607611
204204 void initializeMergeFunctionsPass(PassRegistry&);
205205 void initializeModuleDebugInfoPrinterPass(PassRegistry&);
206206 void initializeNoAAPass(PassRegistry&);
207 void initializeNoopInsertionPass(PassRegistry&);
207208 void initializeObjCARCAliasAnalysisPass(PassRegistry&);
208209 void initializeObjCARCAPElimPass(PassRegistry&);
209210 void initializeObjCARCExpandPass(PassRegistry&);
3030 /// module.
3131 class RandomNumberGenerator {
3232 public:
33 typedef std::mt19937_64 RNG;
34 typedef RNG::result_type result_type;
35
3336 /// Returns a random number in the range [0, Max).
34 uint_fast64_t operator()();
37 result_type operator()();
38
39 // Must define min and max to be compatible with URNG as used by
40 // std::uniform_*_distribution
41 static LLVM_CONSTEXPR result_type min() {
42 return RNG::min();
43 }
44 static LLVM_CONSTEXPR result_type max() {
45 return RNG::max();
46 }
3547
3648 private:
3749 /// Seeds and salts the underlying RNG engine.
4456 // http://en.cppreference.com/w/cpp/numeric/random/mersenne_twister_engine
4557 // This RNG is deterministically portable across C++11
4658 // implementations.
47 std::mt19937_64 Generator;
59 RNG Generator;
4860
4961 // Noncopyable.
5062 RandomNumberGenerator(const RandomNumberGenerator &other)
3131 class MCInst;
3232 struct MCSchedModel;
3333 class MCSymbolRefExpr;
34 class RandomNumberGenerator;
3435 class SDNode;
3536 class ScheduleHazardRecognizer;
3637 class SelectionDAG;
874875 virtual void insertNoop(MachineBasicBlock &MBB,
875876 MachineBasicBlock::iterator MI) const;
876877
878 /// insertNoop - Insert a type of noop into the instruction stream at the
879 /// specified point to introduce fine-grained diversity. A target may randomly
880 /// choose from a pool of valid noops using the provided RNG.
881 virtual void insertNoop(MachineBasicBlock &MBB,
882 MachineBasicBlock::iterator MI,
883 RandomNumberGenerator&) const {
884 insertNoop(MBB, MI);
885 }
877886
878887 /// Return the noop instruction to use for a noop.
879888 virtual void getNoopForMachoTarget(MCInst &NopInst) const;
7777 EnableFastISel(false), PositionIndependentExecutable(false),
7878 UseInitArray(false), DisableIntegratedAS(false),
7979 CompressDebugSections(false), FunctionSections(false),
80 DataSections(false), TrapUnreachable(false), TrapFuncName(),
81 FloatABIType(FloatABI::Default),
80 DataSections(false), NoopInsertion(false), TrapUnreachable(false),
81 TrapFuncName(), FloatABIType(FloatABI::Default),
8282 AllowFPOpFusion(FPOpFusion::Standard), JTType(JumpTable::Single),
8383 FCFI(false), ThreadModel(ThreadModel::POSIX),
8484 CFIType(CFIntegrity::Sub), CFIEnforcing(false), CFIFuncName() {}
196196
197197 /// Emit data into separate sections.
198198 unsigned DataSections : 1;
199
200 /// Randomly insert noop instructions to create fine-grained code
201 /// layout diversity.
202 unsigned NoopInsertion : 1;
199203
200204 /// Emit target-specific trap instruction for 'unreachable' IR instructions.
201205 unsigned TrapUnreachable : 1;
7070 MachineSink.cpp
7171 MachineTraceMetrics.cpp
7272 MachineVerifier.cpp
73 NoopInsertion.cpp
7374 OcamlGC.cpp
7475 OptimizePHIs.cpp
7576 PHIElimination.cpp
5050 initializeMachineSchedulerPass(Registry);
5151 initializeMachineSinkingPass(Registry);
5252 initializeMachineVerifierPassPass(Registry);
53 initializeNoopInsertionPass(Registry);
5354 initializeOptimizePHIsPass(Registry);
5455 initializePHIEliminationPass(Registry);
5556 initializePeepholeOptimizerPass(Registry);
0 //===- NoopInsertion.cpp - Noop Insertion ---------------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This pass adds fine-grained diversity by displacing code using randomly
10 // placed (optionally target supplied) Noop instructions.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "llvm/CodeGen/NoopInsertion.h"
15 #include "llvm/ADT/Statistic.h"
16 #include "llvm/CodeGen/MachineInstrBuilder.h"
17 #include "llvm/CodeGen/MachineModuleInfo.h"
18 #include "llvm/CodeGen/MachineRegisterInfo.h"
19 #include "llvm/CodeGen/Passes.h"
20 #include "llvm/IR/BasicBlock.h"
21 #include "llvm/IR/Function.h"
22 #include "llvm/IR/Module.h"
23 #include "llvm/Support/Allocator.h"
24 #include "llvm/Support/CommandLine.h"
25 #include "llvm/Support/RandomNumberGenerator.h"
26 #include "llvm/Target/TargetInstrInfo.h"
27 using namespace llvm;
28
29 #define DEBUG_TYPE "noop-insertion"
30
31 static cl::opt NoopInsertionPercentage(
32 "noop-insertion-percentage",
33 cl::desc("Percentage of instructions that have Noops prepended"),
34 cl::init(25)); // Default is a good balance between entropy and
35 // performance impact
36
37 static cl::opt MaxNoopsPerInstruction(
38 "max-noops-per-instruction",
39 llvm::cl::desc("Maximum number of Noops per instruction"),
40 llvm::cl::init(1));
41
42 STATISTIC(InsertedNoops,
43 "Total number of noop type instructions inserted for diversity");
44
45 char NoopInsertion::ID = 0;
46 char &llvm::NoopInsertionID = NoopInsertion::ID;
47 INITIALIZE_PASS(NoopInsertion, "noop-insertion",
48 "Noop Insertion for fine-grained code randomization", false,
49 false)
50
51 NoopInsertion::NoopInsertion() : MachineFunctionPass(ID) {
52 initializeNoopInsertionPass(*PassRegistry::getPassRegistry());
53
54 // clamp percentage to 100
55 if (NoopInsertionPercentage > 100)
56 NoopInsertionPercentage = 100;
57 }
58
59 void NoopInsertion::getAnalysisUsage(AnalysisUsage &AU) const {
60 AU.setPreservesCFG();
61 MachineFunctionPass::getAnalysisUsage(AU);
62 }
63
64 bool NoopInsertion::runOnMachineFunction(MachineFunction &Fn) {
65 // The RNG must be initialized on first use so we have a Module to
66 // construct it from
67 if (!RNG)
68 RNG.reset(Fn.getFunction()->getParent()->createRNG(this));
69
70 const TargetInstrInfo *TII = Fn.getSubtarget().getInstrInfo();
71
72 unsigned FnInsertedNoopCount = 0;
73
74 for (auto &BB : Fn) {
75 MachineBasicBlock::iterator FirstTerm = BB.getFirstTerminator();
76
77 for (MachineBasicBlock::iterator I = BB.begin(), E = BB.end(); I != E;
78 ++I) {
79 if (I->isPseudo())
80 continue;
81
82 // Insert random number of Noop-like instructions.
83 for (unsigned i = 0; i < MaxNoopsPerInstruction; i++) {
84 if (Distribution(*RNG) >= NoopInsertionPercentage)
85 continue;
86
87 TII->insertNoop(BB, I, *RNG);
88
89 ++FnInsertedNoopCount;
90 }
91
92 if (I == FirstTerm)
93 break;
94 }
95 }
96
97 InsertedNoops += FnInsertedNoopCount;
98
99 return FnInsertedNoopCount > 0;
100 }
582582 addPass(createGCInfoPrinter(dbgs()), false, false);
583583 }
584584
585 if (TM->Options.NoopInsertion)
586 addPass(&NoopInsertionID);
587
585588 // Basic block placement.
586589 if (getOptLevel() != CodeGenOpt::None)
587590 addBlockPlacement();
3333 #include "llvm/Support/CommandLine.h"
3434 #include "llvm/Support/Debug.h"
3535 #include "llvm/Support/ErrorHandling.h"
36 #include "llvm/Support/RandomNumberGenerator.h"
3637 #include "llvm/Support/raw_ostream.h"
3738 #include "llvm/Target/TargetOptions.h"
3839 #include
56195620 MI->setDesc(get(table[Domain-1]));
56205621 }
56215622
5623 /// insertNoop - Insert a noop into the instruction stream at the specified
5624 /// point.
5625 void X86InstrInfo::insertNoop(MachineBasicBlock &MBB,
5626 MachineBasicBlock::iterator MI) const {
5627 DebugLoc DL;
5628 BuildMI(MBB, MI, DL, get(X86::NOOP));
5629 }
5630
5631 /// insertNoop - Insert a randomly chosen type of noop into the instruction
5632 /// stream at the specified point to introduce fine-grained diversity.
5633 void X86InstrInfo::insertNoop(MachineBasicBlock &MBB,
5634 MachineBasicBlock::iterator MI,
5635 RandomNumberGenerator &RNG) const {
5636 // This set of Noop instructions was carefully chosen so that
5637 // misaligned parses of these instructions do not introduce new,
5638 // useful ROP gadgets. The ASM instructions noted are for misaligned
5639 // parses of the noop in 32 and 64 bits.
5640 enum {
5641 NOP, // 90
5642 MOV_BP, // 89 ed, 48 89 ed -- IN EAX, IN AL (privileged)
5643 MOV_SP, // 89 e4, 48 89 e4 -- IN AL, IN EAX (privileged)
5644 LEA_SI, // 8d 36, 48 8d 36 -- SS segment override, NULL
5645 // prefix (does not add new gadget)
5646 LEA_DI, // 8d 3f, 48 8d 3f -- AAS (bcd->hex), invalid
5647 MAX_NOPS
5648 };
5649
5650 static const unsigned NopRegs[MAX_NOPS][2] = {
5651 {0, 0},
5652 {X86::EBP, X86::RBP},
5653 {X86::ESP, X86::RSP},
5654 {X86::ESI, X86::RSI},
5655 {X86::EDI, X86::RDI},
5656 };
5657
5658 std::uniform_int_distribution Distribution(0, MAX_NOPS - 1);
5659
5660 unsigned Type = Distribution(RNG);
5661
5662 DebugLoc DL;
5663 bool is64Bit = Subtarget.is64Bit();
5664 unsigned Reg = NopRegs[Type][is64Bit];
5665
5666 switch (Type) {
5667 case NOP:
5668 BuildMI(MBB, MI, DL, get(X86::NOOP));
5669 break;
5670 case MOV_BP:
5671 case MOV_SP:
5672 copyPhysReg(MBB, MI, DL, Reg, Reg, false);
5673 break;
5674 case LEA_SI:
5675 case LEA_DI: {
5676 unsigned opc = is64Bit ? X86::LEA64r : X86::LEA32r;
5677 addRegOffset(BuildMI(MBB, MI, DL, get(opc), Reg), Reg, false, 0);
5678 break;
5679 }
5680 }
5681 }
5682
56225683 /// getNoopForMachoTarget - Return the noop instruction to use for a noop.
56235684 void X86InstrInfo::getNoopForMachoTarget(MCInst &NopInst) const {
56245685 NopInst.setOpcode(X86::NOOP);
360360 bool shouldScheduleAdjacent(MachineInstr* First,
361361 MachineInstr *Second) const override;
362362
363 void insertNoop(MachineBasicBlock &MBB,
364 MachineBasicBlock::iterator MI) const override;
365
366 void insertNoop(MachineBasicBlock &MBB,
367 MachineBasicBlock::iterator MI,
368 RandomNumberGenerator &RNG) const override;
369
363370 void getNoopForMachoTarget(MCInst &NopInst) const override;
364371
365372 bool
0 ; RUN: llc < %s -march=mips -noop-insertion | FileCheck %s
1 ; RUN: llc < %s -march=mips -noop-insertion -rng-seed=1 | FileCheck %s --check-prefix=SEED1
2 ; RUN: llc < %s -march=mips -noop-insertion -noop-insertion-percentage=100 | FileCheck %s --check-prefix=100PERCENT
3
4 ; This test case checks that NOOPs are inserted correctly for MIPS.
5
6 ; It just happens that with a default percentage of 25% and seed=0,
7 ; no NOOPs are inserted.
8 ; CHECK: mul
9 ; CHECK-NEXT: jr
10
11 ; SEED1: nop
12 ; SEED1-NEXT: mul
13 ; SEED1-NEXT: jr
14
15 ; 100PERCENT: nop
16 ; 100PERCENT-NEXT: mul
17 ; 100PERCENT-NEXT: nop
18 ; 100PERCENT-NEXT: jr
19
20 define i32 @test1(i32 %x, i32 %y, i32 %z) {
21 entry:
22 %tmp = mul i32 %x, %y
23 %tmp2 = add i32 %tmp, %z
24 ret i32 %tmp2
25 }
0 ; RUN: llc < %s -march=ppc32 -noop-insertion | FileCheck %s
1 ; RUN: llc < %s -march=ppc32 -noop-insertion -rng-seed=1 | FileCheck %s --check-prefix=SEED1
2 ; RUN: llc < %s -march=ppc32 -noop-insertion -noop-insertion-percentage=100 | FileCheck %s --check-prefix=100PERCENT
3
4 ; This test case checks that NOOPs are inserted correctly for PowerPC.
5
6 ; It just happens that with a default percentage of 25% and seed=0,
7 ; no NOOPs are inserted.
8 ; CHECK: mullw
9 ; CHECK-NEXT: add
10 ; CHECK-NEXT: blr
11
12 ; SEED1: nop
13 ; SEED1-NEXT: mullw
14 ; SEED1-NEXT: add
15 ; SEED1-NEXT: nop
16 ; SEED1-NEXT: blr
17
18 ; 100PERCENT: nop
19 ; 100PERCENT-NEXT: mullw
20 ; 100PERCENT-NEXT: nop
21 ; 100PERCENT-NEXT: add
22 ; 100PERCENT-NEXT: nop
23 ; 100PERCENT-NEXT: blr
24
25 define i32 @test1(i32 %x, i32 %y, i32 %z) {
26 entry:
27 %tmp = mul i32 %x, %y
28 %tmp2 = add i32 %tmp, %z
29 ret i32 %tmp2
30 }
0 ; RUN: llc < %s -mtriple=x86_64-linux -rng-seed=5 -noop-insertion -noop-insertion-percentage=10 \
1 ; RUN: | FileCheck %s --check-prefix=PERCENT10
2 ; RUN: llc < %s -mtriple=x86_64-linux -rng-seed=5 -noop-insertion -noop-insertion-percentage=50 \
3 ; RUN: | FileCheck %s --check-prefix=PERCENT50
4 ; RUN: llc < %s -mtriple=x86_64-linux -rng-seed=5 -noop-insertion -noop-insertion-percentage=100 \
5 ; RUN: | FileCheck %s --check-prefix=PERCENT100
6
7 ; RUN: llc < %s -march=x86 -rng-seed=5 -noop-insertion -noop-insertion-percentage=100 \
8 ; RUN: | FileCheck %s --check-prefix=X86-PERCENT100
9
10 ; This test case tests NOOP insertion at varying percentage levels.
11
12 define i32 @test(i32 %x, i32 %y, i32 %z) {
13 entry:
14 %t1 = add i32 %x, %y
15 %t2 = mul i32 %t1, %z
16 %t3 = add i32 %t2, %x
17 %t4 = mul i32 %t3, %z
18 %t5 = add i32 %t4, %x
19 %t6 = mul i32 %t5, %z
20 %t7 = add i32 %t6, %x
21 %t8 = mul i32 %t7, %z
22 %t9 = add i32 %t8, %x
23 %t10 = mul i32 %t9, %z
24 %t11 = add i32 %t10, %x
25 ret i32 %t11
26 }
27
28 ; PERCENT10: movq %rbp, %rbp
29 ; PERCENT10: retq
30
31 ; PERCENT50: leaq (%rdi), %rdi
32 ; PERCENT50: nop
33 ; PERCENT50: movq %rbp, %rbp
34 ; PERCENT50: movq %rsp, %rsp
35 ; PERCENT50: leaq (%rsi), %rsi
36 ; PERCENT50: nop
37 ; PERCENT50: retq
38
39 ; PERCENT100: leaq (%rdi), %rdi
40 ; PERCENT100: leaq (%rdi), %rdi
41 ; PERCENT100: nop
42 ; PERCENT100: movq %rbp, %rbp
43 ; PERCENT100: movq %rsp, %rsp
44 ; PERCENT100: nop
45 ; PERCENT100: nop
46 ; PERCENT100: leaq (%rsi), %rsi
47 ; PERCENT100: nop
48 ; PERCENT100: leaq (%rdi), %rdi
49 ; PERCENT100: leaq (%rdi), %rdi
50 ; PERCENT100: leaq (%rsi), %rsi
51 ; PERCENT100: retq
52
53
54 ; X86-PERCENT100: leal (%edi), %edi
55 ; X86-PERCENT100: leal (%edi), %edi
56 ; X86-PERCENT100: nop
57 ; X86-PERCENT100: movl %ebp, %ebp
58 ; X86-PERCENT100: movl %esp, %esp
59 ; X86-PERCENT100: nop
60 ; X86-PERCENT100: nop
61 ; X86-PERCENT100: leal (%esi), %esi
62 ; X86-PERCENT100: nop
63 ; X86-PERCENT100: leal (%edi), %edi
64 ; X86-PERCENT100: leal (%edi), %edi
65 ; X86-PERCENT100: leal (%esi), %esi
0 ; RUN: llc < %s -mtriple=x86_64-linux -noop-insertion | FileCheck %s
1 ; RUN: llc < %s -mtriple=x86_64-linux -noop-insertion -rng-seed=1 | FileCheck %s --check-prefix=SEED1
2 ; RUN: llc < %s -mtriple=x86_64-linux -noop-insertion -rng-seed=20 | FileCheck %s --check-prefix=SEED2
3 ; RUN: llc < %s -mtriple=x86_64-linux -noop-insertion -rng-seed=500 | FileCheck %s --check-prefix=SEED3
4
5 ; RUN: llc < %s -march=x86 -noop-insertion | FileCheck %s --check-prefix=x86_32
6
7 ; This test case checks that NOOPs are inserted, and that the RNG seed
8 ; affects both the placement (position of imull) and choice of these NOOPs.
9
10 ; It just happens that with a default percentage of 25% and seed=0,
11 ; no NOOPs are inserted.
12 ; CHECK: imull
13 ; CHECK-NEXT: leal
14 ; CHECK-NEXT: retq
15 ; CHECK-NOT: nop
16
17 ; SEED1: leaq (%rsi), %rsi
18 ; SEED1-NEXT: imull
19 ; SEED1-NEXT: leal
20 ; SEED1-NEXT: retq
21
22 ; SEED2: imull
23 ; SEED2-NEXT: movq %rsp, %rsp
24 ; SEED2-NEXT: leal
25 ; SEED2-NEXT: retq
26
27 ; SEED3: imull
28 ; SEED3-NEXT: movq %rsp, %rsp
29 ; SEED3-NEXT: leal
30 ; SEED3-NEXT: leaq (%rdi), %rdi
31 ; SEED3-NEXT: retq
32
33 ; The operand of the following is used to distinguish from a movl NOOP
34 ; x86_32: movl 4(%esp),
35 ; x86_32-NEXT: imull
36 ; x86_32-NEXT: addl
37 ; x86_32-NEXT: movl %esp, %esp
38 ; x86_32-NEXT: retl
39
40 define i32 @test1(i32 %x, i32 %y, i32 %z) {
41 entry:
42 %tmp = mul i32 %x, %y
43 %tmp2 = add i32 %tmp, %z
44 ret i32 %tmp2
45 }