Skip to content

Commit b177f06

Browse files
committed
[RISCV] Implement hardening guards for jumps, returns and tail returns
This patch adds support for `unimp` guard instructions following return, tail, and unconditional jump instructions, as a hardening measure.
1 parent 78939eb commit b177f06

File tree

16 files changed

+505
-103
lines changed

16 files changed

+505
-103
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3582,6 +3582,10 @@ def mcmodel_EQ_medany : Flag<["-"], "mcmodel=medany">, Group<m_Group>,
35823582
HelpText<"Equivalent to -mcmodel=medium, compatible with RISC-V gcc.">;
35833583
def menable_experimental_extensions : Flag<["-"], "menable-experimental-extensions">, Group<m_Group>,
35843584
HelpText<"Enable use of experimental RISC-V extensions.">;
3585+
def mguards : Flag<["-"], "mguards">, Group<m_riscv_Features_Group>,
3586+
HelpText<"Enable control flow guards">;
3587+
def mno_guards: Flag<["-"], "mno-guards">, Group<m_riscv_Features_Group>,
3588+
HelpText<"Disable control flow guards">;
35853589

35863590
def munaligned_access : Flag<["-"], "munaligned-access">, Group<m_arm_Features_Group>,
35873591
HelpText<"Allow memory accesses to be unaligned (AArch32/AArch64 only)">;

clang/lib/Driver/ToolChains/Arch/RISCV.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
160160
else
161161
Features.push_back("-save-restore");
162162

163+
if (Args.hasFlag(options::OPT_mguards, options::OPT_mno_guards, false))
164+
Features.push_back("+guards");
165+
else
166+
Features.push_back("-guards");
167+
163168
// Now add any that the user explicitly requested on the command line,
164169
// which may override the defaults.
165170
handleTargetFeaturesGroup(Args, Features, options::OPT_m_riscv_Features_Group);

clang/test/Driver/riscv-features.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@
2222
// DEFAULT-NOT: "-target-feature" "+save-restore"
2323

2424
// RUN: %clang --target=riscv32-linux -### %s -fsyntax-only 2>&1 \
25+
// RUN: %clang -target riscv32-unknown-elf -### %s -mguards 2>&1 | FileCheck %s -check-prefix=GUARDS
26+
// RUN: %clang -target riscv32-unknown-elf -### %s -mno-guards 2>&1 | FileCheck %s -check-prefix=NO-GUARDS
27+
28+
// GUARDS: "-target-feature" "+guards"
29+
// NO-GUARDS: "-target-feature" "-guards"
30+
// DEFAULT: "-target-feature" "-guards"
31+
// DEFAULT-NOT: "-target-feature" "+guards"
32+
33+
// RUN: %clang -target riscv32-linux -### %s -fsyntax-only 2>&1 \
2534
// RUN: | FileCheck %s -check-prefix=DEFAULT-LINUX
2635
// RUN: %clang --target=riscv64-linux -### %s -fsyntax-only 2>&1 \
2736
// RUN: | FileCheck %s -check-prefix=DEFAULT-LINUX

llvm/lib/Target/RISCV/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ add_llvm_target(RISCVCodeGen
3232
RISCVISelLowering.cpp
3333
RISCVMachineFunctionInfo.cpp
3434
RISCVMacroFusion.cpp
35+
RISCVJumpGuardsHardener.cpp
3536
RISCVMCInstLower.cpp
3637
RISCVMergeBaseOffset.cpp
3738
RISCVRedundantCopyElimination.cpp

llvm/lib/Target/RISCV/RISCV.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ void initializeRISCVInsertVSETVLIPass(PassRegistry &);
7171
FunctionPass *createRISCVRedundantCopyEliminationPass();
7272
void initializeRISCVRedundantCopyEliminationPass(PassRegistry &);
7373

74+
FunctionPass *createRISCVJumpGuardsHardenerPass();
75+
void initializeRISCVJumpGuardsHardenerPass(PassRegistry &);
76+
7477
InstructionSelector *createRISCVInstructionSelector(const RISCVTargetMachine &,
7578
RISCVSubtarget &,
7679
RISCVRegisterBankInfo &);

llvm/lib/Target/RISCV/RISCV.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ include "llvm/Target/Target.td"
1414

1515
include "RISCVFeatures.td"
1616

17+
// def FeatureGuards : SubtargetFeature<"guards", "EnableGuards",
18+
// "true", "Enable control flow guards">;
19+
1720
//===----------------------------------------------------------------------===//
1821
// Named operands for CSR instructions.
1922
//===----------------------------------------------------------------------===//

llvm/lib/Target/RISCV/RISCVFeatures.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
// RISC-V subtarget features and instruction predicates.
1111
//===----------------------------------------------------------------------===//
1212

13+
def FeatureGuards
14+
: SubtargetFeature<"guards", "HasGuards", "true",
15+
"Jump guards hardening">;
16+
def HasGuards : Predicate<"Subtarget->hasGuards()">,
17+
AssemblerPredicate<(all_of FeatureGuards),
18+
"Jump guards hardening">;
19+
1320
def FeatureStdExtM
1421
: SubtargetFeature<"m", "HasStdExtM", "true",
1522
"'M' (Integer Multiplication and Division)">;

llvm/lib/Target/RISCV/RISCVInstrInfo.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,13 @@ def WRS_STO : RVInstI<0b000, OPC_SYSTEM, (outs), (ins), "wrs.sto", "">,
744744
}
745745
} // Predicates = [HasStdExtZawrs]
746746

747+
let isTerminator = 1, isCodeGenOnly = 1 in
748+
def GUARD_UNIMP : RVInstI<0b001, OPC_SYSTEM, (outs), (ins), "unimp", "">,
749+
Sched<[]> {
750+
let rs1 = 0;
751+
let rd = 0;
752+
let imm12 = 0b110000000000;
753+
}
747754
} // hasSideEffects = 1, mayLoad = 0, mayStore = 0
748755

749756
def CSRRW : CSR_ir<0b001, "csrrw">;
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//===--- RISCVJumpGuardsHardener.cpp - Guard jumps hardening --------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "RISCV.h"
10+
#include "RISCVTargetMachine.h"
11+
#include "llvm/CodeGen/MachineFunctionPass.h"
12+
#include "llvm/CodeGen/Passes.h"
13+
#include "llvm/Support/Debug.h"
14+
#include "llvm/Target/TargetOptions.h"
15+
using namespace llvm;
16+
17+
#define DEBUG_TYPE "riscv-jump-guards"
18+
#define RISCV_JUMP_GUARDS_NAME "RISCV Jump Guards Hardening"
19+
namespace {
20+
21+
struct RISCVJumpGuardsHardener : public MachineFunctionPass {
22+
private:
23+
const RISCVSubtarget *ST = nullptr;
24+
25+
public:
26+
static char ID;
27+
bool runOnMachineFunction(MachineFunction &Fn) override;
28+
29+
RISCVJumpGuardsHardener() : MachineFunctionPass(ID) {}
30+
31+
StringRef getPassName() const override {
32+
return RISCV_JUMP_GUARDS_NAME;
33+
}
34+
35+
private:
36+
MachineRegisterInfo *MRI;
37+
};
38+
} // end anonymous namespace
39+
40+
char RISCVJumpGuardsHardener::ID = 0;
41+
INITIALIZE_PASS(RISCVJumpGuardsHardener, DEBUG_TYPE,
42+
RISCV_JUMP_GUARDS_NAME, false, false)
43+
44+
// Indicates if this is a jump instruction that is protected by jump guards
45+
// (e.g. `unimp` instructions) when that option is enabled.
46+
static bool hasJumpGuards(const MachineInstr &MI) {
47+
switch (MI.getOpcode()) {
48+
default:
49+
return false;
50+
case RISCV::PseudoTAIL:
51+
case RISCV::PseudoJump:
52+
case RISCV::PseudoRET:
53+
case RISCV::PseudoBR:
54+
return true;
55+
case RISCV::C_J: // J pseudo-instruction/alias expansion.
56+
return true;
57+
case RISCV::JALR: {
58+
// Check for possible RET pseudo-instruction (PseudoRET) expansion.
59+
MCRegister Rd = MI.getOperand(0).getReg();
60+
MCRegister Rs = MI.getOperand(1).getReg();
61+
return Rd == RISCV::X0 && Rs == RISCV::X1;
62+
}
63+
case RISCV::C_JR: {
64+
// Check for possible RET pseudo-instruction (PseudoRET) expansion.
65+
MCRegister Rs = MI.getOperand(0).getReg();
66+
return Rs == RISCV::X1;
67+
}
68+
case RISCV::JAL: {
69+
// Check for possible J pseudo-instruction/alias expansion.
70+
MCRegister Rd = MI.getOperand(0).getReg();
71+
return Rd == RISCV::X0;
72+
}
73+
}
74+
}
75+
76+
bool RISCVJumpGuardsHardener::runOnMachineFunction(MachineFunction &Fn) {
77+
if (skipFunction(Fn.getFunction()))
78+
return false;
79+
80+
ST = &Fn.getSubtarget<RISCVSubtarget>();
81+
if (!ST->hasFeature(RISCV::FeatureGuards))
82+
return false;
83+
84+
const RISCVInstrInfo *TII;
85+
TII = static_cast<const RISCVInstrInfo *>(Fn.getSubtarget().getInstrInfo());
86+
87+
bool MadeChange = false;
88+
MRI = &Fn.getRegInfo();
89+
for (MachineBasicBlock &MBB : Fn) {
90+
MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
91+
while (MBBI != E) {
92+
MachineBasicBlock::iterator NMBBI = std::next(MBBI);
93+
MachineInstr &MI = *MBBI;
94+
MBBI = NMBBI;
95+
if (hasJumpGuards(MI)) {
96+
DebugLoc DL = MI.getDebugLoc();
97+
auto HardeningMBB = Fn.CreateMachineBasicBlock(MBB.getBasicBlock());
98+
Fn.insert(++MBB.getIterator(), HardeningMBB);
99+
MachineBasicBlock::iterator HMBBI = HardeningMBB->begin();
100+
BuildMI(*HardeningMBB, HMBBI, DL, TII->get(RISCV::GUARD_UNIMP));
101+
BuildMI(*HardeningMBB, HMBBI, DL, TII->get(RISCV::GUARD_UNIMP));
102+
MadeChange |= true;
103+
break;
104+
}
105+
}
106+
}
107+
108+
return MadeChange;
109+
}
110+
111+
FunctionPass *llvm::createRISCVJumpGuardsHardenerPass() {
112+
return new RISCVJumpGuardsHardener();
113+
}

llvm/lib/Target/RISCV/RISCVTargetMachine.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
8282
initializeRISCVExpandPseudoPass(*PR);
8383
initializeRISCVInsertVSETVLIPass(*PR);
8484
initializeRISCVDAGToDAGISelPass(*PR);
85+
initializeRISCVJumpGuardsHardenerPass(*PR);
8586
}
8687

8788
static StringRef computeDataLayout(const Triple &TT) {
@@ -332,6 +333,8 @@ void RISCVPassConfig::addPreEmitPass() {
332333

333334
void RISCVPassConfig::addPreEmitPass2() {
334335
addPass(createRISCVExpandPseudoPass());
336+
addPass(createRISCVJumpGuardsHardenerPass());
337+
addPass(&BranchRelaxationPassID);
335338
// Schedule the expansion of AMOs at the last possible moment, avoiding the
336339
// possibility for other passes to break the requirements for forward
337340
// progress in the LR/SC block.

llvm/test/CodeGen/RISCV/O0-pipeline.ll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
; CHECK-NEXT: Machine Optimization Remark Emitter
6464
; CHECK-NEXT: Stack Frame Layout Analysis
6565
; CHECK-NEXT: RISCV pseudo instruction expansion pass
66+
; CHECK-NEXT: RISCV Jump Guards Hardening
67+
; CHECK-NEXT: Branch relaxation pass
6668
; CHECK-NEXT: RISCV atomic pseudo instruction expansion pass
6769
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
6870
; CHECK-NEXT: Machine Optimization Remark Emitter

llvm/test/CodeGen/RISCV/O3-pipeline.ll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@
174174
; CHECK-NEXT: Machine Optimization Remark Emitter
175175
; CHECK-NEXT: Stack Frame Layout Analysis
176176
; CHECK-NEXT: RISCV pseudo instruction expansion pass
177+
; CHECK-NEXT: RISCV Jump Guards Hardening
178+
; CHECK-NEXT: Branch relaxation pass
177179
; CHECK-NEXT: RISCV atomic pseudo instruction expansion pass
178180
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
179181
; CHECK-NEXT: Machine Optimization Remark Emitter

0 commit comments

Comments
 (0)