Skip to content

Commit

Permalink
[ARM] Mitigate the cve-2021-35465 security vulnurability.
Browse files Browse the repository at this point in the history
Recently a vulnerability issue is found in the implementation of VLLDM
instruction in the Arm Cortex-M33, Cortex-M35P and Cortex-M55. If the
VLLDM instruction is abandoned due to an exception when it is partially
completed, it is possible for subsequent non-secure handler to access
and modify the partial restored register values. This vulnerability is
identified as CVE-2021-35465.

The mitigation sequence varies between v8-m and v8.1-m as follows:

v8-m.main
---------
mrs        r5, control
tst        r5, #8       /* CONTROL_S.SFPA */
it         ne
.inst.w    0xeeb00a40   /* vmovne s0, s0 */
1:
vlldm      sp           /* Lazy restore of d0-d16 and FPSCR. */

v8.1-m.main
-----------
vscclrm    {vpr}        /* Clear VPR. */
vlldm      sp           /* Lazy restore of d0-d16 and FPSCR. */

More details on
developer.arm.com/support/arm-security-updates/vlldm-instruction-security-vulnerability

Differential Revision: https://reviews.llvm.org/D109157
  • Loading branch information
labrinea committed Sep 16, 2021
1 parent 61f25da commit 1bd5ea9
Show file tree
Hide file tree
Showing 10 changed files with 323 additions and 10 deletions.
4 changes: 4 additions & 0 deletions clang/docs/ClangCommandLineReference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3228,6 +3228,10 @@ Reserve the r9 register (ARM only)

Allow use of CMSE (Armv8-M Security Extensions)

.. option:: -mfix-cmse-cve-2021-35465, -mno-fix-cmse-cve-2021-35465

Enable the cve-2021-35465 security vulnerability mitigation (ARM only).

.. option:: -mexecute-only, -mno-execute-only, -mpure-code

Disallow generation of data access to code sections (ARM only)
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3279,6 +3279,12 @@ defm aapcs_bitfield_width : BoolOption<"f", "aapcs-bitfield-width",

def mgeneral_regs_only : Flag<["-"], "mgeneral-regs-only">, Group<m_Group>,
HelpText<"Generate code which only uses the general purpose registers (AArch64/x86 only)">;
def mfix_cmse_cve_2021_35465 : Flag<["-"], "mfix-cmse-cve-2021-35465">,
Group<m_arm_Features_Group>,
HelpText<"Work around VLLDM erratum CVE-2021-35465 (ARM only)">;
def mno_fix_cmse_cve_2021_35465 : Flag<["-"], "mno-fix-cmse-cve-2021-35465">,
Group<m_arm_Features_Group>,
HelpText<"Don't work around VLLDM erratum CVE-2021-35465 (ARM only)">;
def mfix_cortex_a53_835769 : Flag<["-"], "mfix-cortex-a53-835769">,
Group<m_aarch64_Features_Group>,
HelpText<"Workaround Cortex-A53 erratum 835769 (AArch64 only)">;
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Driver/ToolChains/Arch/ARM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,18 @@ void arm::getARMTargetFeatures(const Driver &D, const llvm::Triple &Triple,
if (Args.getLastArg(options::OPT_mcmse))
Features.push_back("+8msecext");

if (Arg *A = Args.getLastArg(options::OPT_mfix_cmse_cve_2021_35465,
options::OPT_mno_fix_cmse_cve_2021_35465)) {
if (!Args.getLastArg(options::OPT_mcmse))
D.Diag(diag::err_opt_not_valid_without_opt)
<< A->getOption().getName() << "-mcmse";

if (A->getOption().matches(options::OPT_mfix_cmse_cve_2021_35465))
Features.push_back("+fix-cmse-cve-2021-35465");
else
Features.push_back("-fix-cmse-cve-2021-35465");
}

// Look for the last occurrence of -mlong-calls or -mno-long-calls. If
// neither options are specified, see if we are compiling for kernel/kext and
// decide whether to pass "+long-calls" based on the OS and its version.
Expand Down
45 changes: 45 additions & 0 deletions clang/test/Driver/arm-cmse-cve-2021-35465.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Disable the fix
//
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
// RUN: -mcmse -mno-fix-cmse-cve-2021-35465 2>&1 |\
// RUN: FileCheck %s --check-prefix=CHECK-NOFIX
//
// RUN: %clang --target=arm-arm-none-eabi -march=armv8.1-m.main %s -### \
// RUN: -mcmse -mno-fix-cmse-cve-2021-35465 2>&1 |\
// RUN: FileCheck %s --check-prefix=CHECK-NOFIX
//
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
// RUN: -mcmse -mfix-cmse-cve-2021-35465 -mno-fix-cmse-cve-2021-35465 2>&1 |\
// RUN: FileCheck %s --check-prefix=CHECK-NOFIX
//
// CHECK-NOFIX: "-target-feature" "-fix-cmse-cve-2021-35465"


// Enable the fix
//
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
// RUN: -mcmse -mfix-cmse-cve-2021-35465 2>&1 |\
// RUN: FileCheck %s --check-prefix=CHECK-FIX
//
// RUN: %clang --target=arm-arm-none-eabi -march=armv8.1-m.main %s -### \
// RUN: -mcmse -mfix-cmse-cve-2021-35465 2>&1 |\
// RUN: FileCheck %s --check-prefix=CHECK-FIX
//
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
// RUN: -mcmse -mno-fix-cmse-cve-2021-35465 -mfix-cmse-cve-2021-35465 2>&1 |\
// RUN: FileCheck %s --check-prefix=CHECK-FIX
//
// CHECK-FIX: "-target-feature" "+fix-cmse-cve-2021-35465"


// Diagnose the option when used without -mcmse
//
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
// RUN: -mfix-cmse-cve-2021-35465 2>&1 |\
// RUN: FileCheck %s --check-prefix=CHECK-DIAG
//
// RUN: %clang --target=arm-arm-none-eabi -march=armv8.1-m.main %s -### \
// RUN: -mno-fix-cmse-cve-2021-35465 2>&1 |\
// RUN: FileCheck %s --check-prefix=CHECK-DIAG
//
// CHECK-DIAG: error: option 'm{{.*}}fix-cmse-cve-2021-35465' cannot be specified without '-mcmse'
14 changes: 11 additions & 3 deletions llvm/lib/Target/ARM/ARM.td
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,11 @@ def FeatureLOB : SubtargetFeature<"lob", "HasLOB", "true",
"Enable Low Overhead Branch "
"extensions">;

def FeatureFixCMSE_CVE_2021_35465 : SubtargetFeature<"fix-cmse-cve-2021-35465",
"FixCMSE_CVE_2021_35465", "true",
"Mitigate against the cve-2021-35465 "
"security vulnurability">;

//===----------------------------------------------------------------------===//
// ARM architecture class
//
Expand Down Expand Up @@ -1213,7 +1218,8 @@ def : ProcessorModel<"cortex-m33", CortexM4Model, [ARMv8mMainline,
FeatureHasSlowFPVMLx,
FeatureHasSlowFPVFMx,
FeatureUseMISched,
FeatureHasNoBranchPredictor]>;
FeatureHasNoBranchPredictor,
FeatureFixCMSE_CVE_2021_35465]>;

def : ProcessorModel<"cortex-m35p", CortexM4Model, [ARMv8mMainline,
FeatureDSP,
Expand All @@ -1222,7 +1228,8 @@ def : ProcessorModel<"cortex-m35p", CortexM4Model, [ARMv8mMainline,
FeatureHasSlowFPVMLx,
FeatureHasSlowFPVFMx,
FeatureUseMISched,
FeatureHasNoBranchPredictor]>;
FeatureHasNoBranchPredictor,
FeatureFixCMSE_CVE_2021_35465]>;

def : ProcessorModel<"cortex-m55", CortexM4Model, [ARMv81mMainline,
FeatureDSP,
Expand All @@ -1231,7 +1238,8 @@ def : ProcessorModel<"cortex-m55", CortexM4Model, [ARMv81mMainline,
FeatureHasNoBranchPredictor,
FeaturePrefLoopAlign32,
FeatureHasSlowFPVMLx,
HasMVEFloatOps]>;
HasMVEFloatOps,
FeatureFixCMSE_CVE_2021_35465]>;

def : ProcNoItin<"cortex-a32", [ARMv8a,
FeatureHWDivThumb,
Expand Down
55 changes: 50 additions & 5 deletions llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1534,6 +1534,11 @@ void ARMExpandPseudo::CMSERestoreFPRegsV8(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc &DL,
SmallVectorImpl<unsigned> &AvailableRegs) {

// Keep a scratch register for the mitigation sequence.
unsigned ScratchReg = ARM::NoRegister;
if (STI->fixCMSE_CVE_2021_35465())
ScratchReg = AvailableRegs.pop_back_val();

// Use AvailableRegs to store the fp regs
std::vector<std::tuple<unsigned, unsigned, unsigned>> ClearedFPRegs;
std::vector<unsigned> NonclearedFPRegs;
Expand Down Expand Up @@ -1582,22 +1587,56 @@ void ARMExpandPseudo::CMSERestoreFPRegsV8(
// Push FP regs that cannot be restored via normal registers on the stack
for (unsigned Reg : NonclearedFPRegs) {
if (ARM::DPR_VFP2RegClass.contains(Reg))
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSTRD), Reg)
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSTRD))
.addReg(Reg)
.addReg(ARM::SP)
.addImm((Reg - ARM::D0) * 2)
.add(predOps(ARMCC::AL));
else if (ARM::SPRRegClass.contains(Reg))
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSTRS), Reg)
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSTRS))
.addReg(Reg)
.addReg(ARM::SP)
.addImm(Reg - ARM::S0)
.add(predOps(ARMCC::AL));
}

// Lazy load fp regs from stack.
// This executes as NOP in the absence of floating-point support.
BuildMI(MBB, MBBI, DL, TII->get(ARM::VLLDM))
.addReg(ARM::SP)
.add(predOps(ARMCC::AL));
MachineInstrBuilder VLLDM = BuildMI(MBB, MBBI, DL, TII->get(ARM::VLLDM))
.addReg(ARM::SP)
.add(predOps(ARMCC::AL));

if (STI->fixCMSE_CVE_2021_35465()) {
auto Bundler = MIBundleBuilder(MBB, VLLDM);
// Read the CONTROL register.
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::t2MRS_M))
.addReg(ScratchReg, RegState::Define)
.addImm(20)
.add(predOps(ARMCC::AL)));
// Check bit 3 (SFPA).
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::t2TSTri))
.addReg(ScratchReg)
.addImm(8)
.add(predOps(ARMCC::AL)));
// Emit the IT block.
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::t2IT))
.addImm(ARMCC::NE)
.addImm(8));
// If SFPA is clear jump over to VLLDM, otherwise execute an instruction
// which has no functional effect apart from causing context creation:
// vmovne s0, s0. In the absence of FPU we emit .inst.w 0xeeb00a40,
// which is defined as NOP if not executed.
if (STI->hasFPRegs())
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::VMOVS))
.addReg(ARM::S0, RegState::Define)
.addReg(ARM::S0, RegState::Undef)
.add(predOps(ARMCC::NE)));
else
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::INLINEASM))
.addExternalSymbol(".inst.w 0xeeb00a40")
.addImm(InlineAsm::Extra_HasSideEffects));
finalizeBundle(MBB, Bundler.begin(), Bundler.end());
}

// Restore all FP registers via normal registers
for (const auto &Regs : ClearedFPRegs) {
Expand Down Expand Up @@ -1638,6 +1677,12 @@ void ARMExpandPseudo::CMSERestoreFPRegsV81(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc &DL,
SmallVectorImpl<unsigned> &AvailableRegs) {
if (!definesOrUsesFPReg(*MBBI)) {
if (STI->fixCMSE_CVE_2021_35465()) {
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSCCLRMS))
.add(predOps(ARMCC::AL))
.addReg(ARM::VPR, RegState::Define);
}

// Load FP registers from stack.
BuildMI(MBB, MBBI, DL, TII->get(ARM::VLLDM))
.addReg(ARM::SP)
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/ARM/ARMSubtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,9 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
/// cannot be encoded. For example, ADD r0, r1, #FFFFFFFF -> SUB r0, r1, #1.
bool NegativeImmediates = true;

/// Mitigate against the cve-2021-35465 security vulnurability.
bool FixCMSE_CVE_2021_35465 = false;

/// Harden against Straight Line Speculation for Returns and Indirect
/// Branches.
bool HardenSlsRetBr = false;
Expand Down Expand Up @@ -934,6 +937,8 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
unsigned PhysReg) const override;
unsigned getGPRAllocationOrder(const MachineFunction &MF) const;

bool fixCMSE_CVE_2021_35465() const { return FixCMSE_CVE_2021_35465; }

bool hardenSlsRetBr() const { return HardenSlsRetBr; }
bool hardenSlsBlr() const { return HardenSlsBlr; }
bool hardenSlsNoComdat() const { return HardenSlsNoComdat; }
Expand Down
69 changes: 69 additions & 0 deletions llvm/test/CodeGen/ARM/cmse-cve-2021-35465-return.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
;
; RUN: llc %s -o - -mtriple=thumbv8m.main -verify-machineinstrs \
; RUN: -mattr=+fp-armv8d16sp,+fix-cmse-cve-2021-35465 -float-abi=hard | \
; RUN: FileCheck %s --check-prefix=CHECK-8M-FP-CVE-2021-35465

%indirect = type { double, double, double, double, double, double, double, double }

define %indirect @func(%indirect (float, i32, double, i32, float, i32, float, i32, double, double, double, double, float, float)* %fu, float %a, i32 %b, double %c, i32 %d, float %e, i32 %f, float %g, i32 %h, double %i, double %j, double %k, double %l, float %m, float %n) {
; CHECK-8M-FP-CVE-2021-35465-LABEL: func:
; CHECK-8M-FP-CVE-2021-35465: @ %bb.0: @ %entry
; CHECK-8M-FP-CVE-2021-35465-NEXT: push {r7, lr}
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov lr, r3
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov r12, r0
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov r0, r1
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov r1, r2
; CHECK-8M-FP-CVE-2021-35465-NEXT: ldr r3, [sp, #8]
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov r2, lr
; CHECK-8M-FP-CVE-2021-35465-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, r11}
; CHECK-8M-FP-CVE-2021-35465-NEXT: bic r12, r12, #1
; CHECK-8M-FP-CVE-2021-35465-NEXT: sub sp, #136
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r4, s5
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r11, s0
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r9, r10, d1
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r8, s1
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r7, s4
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r5, r6, d3
; CHECK-8M-FP-CVE-2021-35465-NEXT: vlstm sp
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov s0, r11
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d1, r9, r10
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov s1, r8
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov s4, r7
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d3, r5, r6
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov s5, r4
; CHECK-8M-FP-CVE-2021-35465-NEXT: vldr d4, [sp, #32]
; CHECK-8M-FP-CVE-2021-35465-NEXT: vldr d5, [sp, #40]
; CHECK-8M-FP-CVE-2021-35465-NEXT: vldr d6, [sp, #48]
; CHECK-8M-FP-CVE-2021-35465-NEXT: vldr s14, [sp, #56]
; CHECK-8M-FP-CVE-2021-35465-NEXT: ldr r4, [sp, #64]
; CHECK-8M-FP-CVE-2021-35465-NEXT: bic r4, r4, #159
; CHECK-8M-FP-CVE-2021-35465-NEXT: bic r4, r4, #4026531840
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmsr fpscr, r4
; CHECK-8M-FP-CVE-2021-35465-NEXT: msr apsr_nzcvq, r12
; CHECK-8M-FP-CVE-2021-35465-NEXT: blxns r12
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r9, r10, d0
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d3, [sp, #24]
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r7, r8, d1
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d4, [sp, #32]
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r5, r6, d2
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d5, [sp, #40]
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d6, [sp, #48]
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d7, [sp, #56]
; CHECK-8M-FP-CVE-2021-35465-NEXT: mrs r11, control
; CHECK-8M-FP-CVE-2021-35465-NEXT: tst.w r11, #8
; CHECK-8M-FP-CVE-2021-35465-NEXT: it ne
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmovne.f32 s0, s0
; CHECK-8M-FP-CVE-2021-35465-NEXT: vlldm sp
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d0, r9, r10
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d1, r7, r8
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d2, r5, r6
; CHECK-8M-FP-CVE-2021-35465-NEXT: add sp, #136
; CHECK-8M-FP-CVE-2021-35465-NEXT: pop.w {r4, r5, r6, r7, r8, r9, r10, r11}
; CHECK-8M-FP-CVE-2021-35465-NEXT: pop {r7, pc}
entry:
%res = call %indirect %fu(float %a, i32 %b, double %c, i32 %d, float %e, i32 %f, float %g, i32 %h, double %i, double %j, double %k, double %l, float %m, float %n) #0
ret %indirect %res
}

attributes #0 = { "cmse_nonsecure_call" }
Loading

0 comments on commit 1bd5ea9

Please sign in to comment.