Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 1bd5ea9

Browse files
committed
[ARM] Mitigate the cve-2021-35465 security vulnurability.
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, rust-lang#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
1 parent 61f25da commit 1bd5ea9

File tree

10 files changed

+323
-10
lines changed

10 files changed

+323
-10
lines changed

clang/docs/ClangCommandLineReference.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3228,6 +3228,10 @@ Reserve the r9 register (ARM only)
32283228

32293229
Allow use of CMSE (Armv8-M Security Extensions)
32303230

3231+
.. option:: -mfix-cmse-cve-2021-35465, -mno-fix-cmse-cve-2021-35465
3232+
3233+
Enable the cve-2021-35465 security vulnerability mitigation (ARM only).
3234+
32313235
.. option:: -mexecute-only, -mno-execute-only, -mpure-code
32323236

32333237
Disallow generation of data access to code sections (ARM only)

clang/include/clang/Driver/Options.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3279,6 +3279,12 @@ defm aapcs_bitfield_width : BoolOption<"f", "aapcs-bitfield-width",
32793279

32803280
def mgeneral_regs_only : Flag<["-"], "mgeneral-regs-only">, Group<m_Group>,
32813281
HelpText<"Generate code which only uses the general purpose registers (AArch64/x86 only)">;
3282+
def mfix_cmse_cve_2021_35465 : Flag<["-"], "mfix-cmse-cve-2021-35465">,
3283+
Group<m_arm_Features_Group>,
3284+
HelpText<"Work around VLLDM erratum CVE-2021-35465 (ARM only)">;
3285+
def mno_fix_cmse_cve_2021_35465 : Flag<["-"], "mno-fix-cmse-cve-2021-35465">,
3286+
Group<m_arm_Features_Group>,
3287+
HelpText<"Don't work around VLLDM erratum CVE-2021-35465 (ARM only)">;
32823288
def mfix_cortex_a53_835769 : Flag<["-"], "mfix-cortex-a53-835769">,
32833289
Group<m_aarch64_Features_Group>,
32843290
HelpText<"Workaround Cortex-A53 erratum 835769 (AArch64 only)">;

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,18 @@ void arm::getARMTargetFeatures(const Driver &D, const llvm::Triple &Triple,
705705
if (Args.getLastArg(options::OPT_mcmse))
706706
Features.push_back("+8msecext");
707707

708+
if (Arg *A = Args.getLastArg(options::OPT_mfix_cmse_cve_2021_35465,
709+
options::OPT_mno_fix_cmse_cve_2021_35465)) {
710+
if (!Args.getLastArg(options::OPT_mcmse))
711+
D.Diag(diag::err_opt_not_valid_without_opt)
712+
<< A->getOption().getName() << "-mcmse";
713+
714+
if (A->getOption().matches(options::OPT_mfix_cmse_cve_2021_35465))
715+
Features.push_back("+fix-cmse-cve-2021-35465");
716+
else
717+
Features.push_back("-fix-cmse-cve-2021-35465");
718+
}
719+
708720
// Look for the last occurrence of -mlong-calls or -mno-long-calls. If
709721
// neither options are specified, see if we are compiling for kernel/kext and
710722
// decide whether to pass "+long-calls" based on the OS and its version.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Disable the fix
2+
//
3+
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
4+
// RUN: -mcmse -mno-fix-cmse-cve-2021-35465 2>&1 |\
5+
// RUN: FileCheck %s --check-prefix=CHECK-NOFIX
6+
//
7+
// RUN: %clang --target=arm-arm-none-eabi -march=armv8.1-m.main %s -### \
8+
// RUN: -mcmse -mno-fix-cmse-cve-2021-35465 2>&1 |\
9+
// RUN: FileCheck %s --check-prefix=CHECK-NOFIX
10+
//
11+
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
12+
// RUN: -mcmse -mfix-cmse-cve-2021-35465 -mno-fix-cmse-cve-2021-35465 2>&1 |\
13+
// RUN: FileCheck %s --check-prefix=CHECK-NOFIX
14+
//
15+
// CHECK-NOFIX: "-target-feature" "-fix-cmse-cve-2021-35465"
16+
17+
18+
// Enable the fix
19+
//
20+
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
21+
// RUN: -mcmse -mfix-cmse-cve-2021-35465 2>&1 |\
22+
// RUN: FileCheck %s --check-prefix=CHECK-FIX
23+
//
24+
// RUN: %clang --target=arm-arm-none-eabi -march=armv8.1-m.main %s -### \
25+
// RUN: -mcmse -mfix-cmse-cve-2021-35465 2>&1 |\
26+
// RUN: FileCheck %s --check-prefix=CHECK-FIX
27+
//
28+
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
29+
// RUN: -mcmse -mno-fix-cmse-cve-2021-35465 -mfix-cmse-cve-2021-35465 2>&1 |\
30+
// RUN: FileCheck %s --check-prefix=CHECK-FIX
31+
//
32+
// CHECK-FIX: "-target-feature" "+fix-cmse-cve-2021-35465"
33+
34+
35+
// Diagnose the option when used without -mcmse
36+
//
37+
// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main %s -### \
38+
// RUN: -mfix-cmse-cve-2021-35465 2>&1 |\
39+
// RUN: FileCheck %s --check-prefix=CHECK-DIAG
40+
//
41+
// RUN: %clang --target=arm-arm-none-eabi -march=armv8.1-m.main %s -### \
42+
// RUN: -mno-fix-cmse-cve-2021-35465 2>&1 |\
43+
// RUN: FileCheck %s --check-prefix=CHECK-DIAG
44+
//
45+
// CHECK-DIAG: error: option 'm{{.*}}fix-cmse-cve-2021-35465' cannot be specified without '-mcmse'

llvm/lib/Target/ARM/ARM.td

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,11 @@ def FeatureLOB : SubtargetFeature<"lob", "HasLOB", "true",
437437
"Enable Low Overhead Branch "
438438
"extensions">;
439439

440+
def FeatureFixCMSE_CVE_2021_35465 : SubtargetFeature<"fix-cmse-cve-2021-35465",
441+
"FixCMSE_CVE_2021_35465", "true",
442+
"Mitigate against the cve-2021-35465 "
443+
"security vulnurability">;
444+
440445
//===----------------------------------------------------------------------===//
441446
// ARM architecture class
442447
//
@@ -1213,7 +1218,8 @@ def : ProcessorModel<"cortex-m33", CortexM4Model, [ARMv8mMainline,
12131218
FeatureHasSlowFPVMLx,
12141219
FeatureHasSlowFPVFMx,
12151220
FeatureUseMISched,
1216-
FeatureHasNoBranchPredictor]>;
1221+
FeatureHasNoBranchPredictor,
1222+
FeatureFixCMSE_CVE_2021_35465]>;
12171223

12181224
def : ProcessorModel<"cortex-m35p", CortexM4Model, [ARMv8mMainline,
12191225
FeatureDSP,
@@ -1222,7 +1228,8 @@ def : ProcessorModel<"cortex-m35p", CortexM4Model, [ARMv8mMainline,
12221228
FeatureHasSlowFPVMLx,
12231229
FeatureHasSlowFPVFMx,
12241230
FeatureUseMISched,
1225-
FeatureHasNoBranchPredictor]>;
1231+
FeatureHasNoBranchPredictor,
1232+
FeatureFixCMSE_CVE_2021_35465]>;
12261233

12271234
def : ProcessorModel<"cortex-m55", CortexM4Model, [ARMv81mMainline,
12281235
FeatureDSP,
@@ -1231,7 +1238,8 @@ def : ProcessorModel<"cortex-m55", CortexM4Model, [ARMv81mMainline,
12311238
FeatureHasNoBranchPredictor,
12321239
FeaturePrefLoopAlign32,
12331240
FeatureHasSlowFPVMLx,
1234-
HasMVEFloatOps]>;
1241+
HasMVEFloatOps,
1242+
FeatureFixCMSE_CVE_2021_35465]>;
12351243

12361244
def : ProcNoItin<"cortex-a32", [ARMv8a,
12371245
FeatureHWDivThumb,

llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,11 @@ void ARMExpandPseudo::CMSERestoreFPRegsV8(
15341534
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc &DL,
15351535
SmallVectorImpl<unsigned> &AvailableRegs) {
15361536

1537+
// Keep a scratch register for the mitigation sequence.
1538+
unsigned ScratchReg = ARM::NoRegister;
1539+
if (STI->fixCMSE_CVE_2021_35465())
1540+
ScratchReg = AvailableRegs.pop_back_val();
1541+
15371542
// Use AvailableRegs to store the fp regs
15381543
std::vector<std::tuple<unsigned, unsigned, unsigned>> ClearedFPRegs;
15391544
std::vector<unsigned> NonclearedFPRegs;
@@ -1582,22 +1587,56 @@ void ARMExpandPseudo::CMSERestoreFPRegsV8(
15821587
// Push FP regs that cannot be restored via normal registers on the stack
15831588
for (unsigned Reg : NonclearedFPRegs) {
15841589
if (ARM::DPR_VFP2RegClass.contains(Reg))
1585-
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSTRD), Reg)
1590+
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSTRD))
1591+
.addReg(Reg)
15861592
.addReg(ARM::SP)
15871593
.addImm((Reg - ARM::D0) * 2)
15881594
.add(predOps(ARMCC::AL));
15891595
else if (ARM::SPRRegClass.contains(Reg))
1590-
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSTRS), Reg)
1596+
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSTRS))
1597+
.addReg(Reg)
15911598
.addReg(ARM::SP)
15921599
.addImm(Reg - ARM::S0)
15931600
.add(predOps(ARMCC::AL));
15941601
}
15951602

15961603
// Lazy load fp regs from stack.
15971604
// This executes as NOP in the absence of floating-point support.
1598-
BuildMI(MBB, MBBI, DL, TII->get(ARM::VLLDM))
1599-
.addReg(ARM::SP)
1600-
.add(predOps(ARMCC::AL));
1605+
MachineInstrBuilder VLLDM = BuildMI(MBB, MBBI, DL, TII->get(ARM::VLLDM))
1606+
.addReg(ARM::SP)
1607+
.add(predOps(ARMCC::AL));
1608+
1609+
if (STI->fixCMSE_CVE_2021_35465()) {
1610+
auto Bundler = MIBundleBuilder(MBB, VLLDM);
1611+
// Read the CONTROL register.
1612+
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::t2MRS_M))
1613+
.addReg(ScratchReg, RegState::Define)
1614+
.addImm(20)
1615+
.add(predOps(ARMCC::AL)));
1616+
// Check bit 3 (SFPA).
1617+
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::t2TSTri))
1618+
.addReg(ScratchReg)
1619+
.addImm(8)
1620+
.add(predOps(ARMCC::AL)));
1621+
// Emit the IT block.
1622+
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::t2IT))
1623+
.addImm(ARMCC::NE)
1624+
.addImm(8));
1625+
// If SFPA is clear jump over to VLLDM, otherwise execute an instruction
1626+
// which has no functional effect apart from causing context creation:
1627+
// vmovne s0, s0. In the absence of FPU we emit .inst.w 0xeeb00a40,
1628+
// which is defined as NOP if not executed.
1629+
if (STI->hasFPRegs())
1630+
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::VMOVS))
1631+
.addReg(ARM::S0, RegState::Define)
1632+
.addReg(ARM::S0, RegState::Undef)
1633+
.add(predOps(ARMCC::NE)));
1634+
else
1635+
Bundler.append(BuildMI(*MBB.getParent(), DL, TII->get(ARM::INLINEASM))
1636+
.addExternalSymbol(".inst.w 0xeeb00a40")
1637+
.addImm(InlineAsm::Extra_HasSideEffects));
1638+
finalizeBundle(MBB, Bundler.begin(), Bundler.end());
1639+
}
16011640

16021641
// Restore all FP registers via normal registers
16031642
for (const auto &Regs : ClearedFPRegs) {
@@ -1638,6 +1677,12 @@ void ARMExpandPseudo::CMSERestoreFPRegsV81(
16381677
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc &DL,
16391678
SmallVectorImpl<unsigned> &AvailableRegs) {
16401679
if (!definesOrUsesFPReg(*MBBI)) {
1680+
if (STI->fixCMSE_CVE_2021_35465()) {
1681+
BuildMI(MBB, MBBI, DL, TII->get(ARM::VSCCLRMS))
1682+
.add(predOps(ARMCC::AL))
1683+
.addReg(ARM::VPR, RegState::Define);
1684+
}
1685+
16411686
// Load FP registers from stack.
16421687
BuildMI(MBB, MBBI, DL, TII->get(ARM::VLLDM))
16431688
.addReg(ARM::SP)

llvm/lib/Target/ARM/ARMSubtarget.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,9 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
468468
/// cannot be encoded. For example, ADD r0, r1, #FFFFFFFF -> SUB r0, r1, #1.
469469
bool NegativeImmediates = true;
470470

471+
/// Mitigate against the cve-2021-35465 security vulnurability.
472+
bool FixCMSE_CVE_2021_35465 = false;
473+
471474
/// Harden against Straight Line Speculation for Returns and Indirect
472475
/// Branches.
473476
bool HardenSlsRetBr = false;
@@ -934,6 +937,8 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
934937
unsigned PhysReg) const override;
935938
unsigned getGPRAllocationOrder(const MachineFunction &MF) const;
936939

940+
bool fixCMSE_CVE_2021_35465() const { return FixCMSE_CVE_2021_35465; }
941+
937942
bool hardenSlsRetBr() const { return HardenSlsRetBr; }
938943
bool hardenSlsBlr() const { return HardenSlsBlr; }
939944
bool hardenSlsNoComdat() const { return HardenSlsNoComdat; }
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
;
3+
; RUN: llc %s -o - -mtriple=thumbv8m.main -verify-machineinstrs \
4+
; RUN: -mattr=+fp-armv8d16sp,+fix-cmse-cve-2021-35465 -float-abi=hard | \
5+
; RUN: FileCheck %s --check-prefix=CHECK-8M-FP-CVE-2021-35465
6+
7+
%indirect = type { double, double, double, double, double, double, double, double }
8+
9+
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) {
10+
; CHECK-8M-FP-CVE-2021-35465-LABEL: func:
11+
; CHECK-8M-FP-CVE-2021-35465: @ %bb.0: @ %entry
12+
; CHECK-8M-FP-CVE-2021-35465-NEXT: push {r7, lr}
13+
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov lr, r3
14+
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov r12, r0
15+
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov r0, r1
16+
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov r1, r2
17+
; CHECK-8M-FP-CVE-2021-35465-NEXT: ldr r3, [sp, #8]
18+
; CHECK-8M-FP-CVE-2021-35465-NEXT: mov r2, lr
19+
; CHECK-8M-FP-CVE-2021-35465-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, r11}
20+
; CHECK-8M-FP-CVE-2021-35465-NEXT: bic r12, r12, #1
21+
; CHECK-8M-FP-CVE-2021-35465-NEXT: sub sp, #136
22+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r4, s5
23+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r11, s0
24+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r9, r10, d1
25+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r8, s1
26+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r7, s4
27+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r5, r6, d3
28+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vlstm sp
29+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov s0, r11
30+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d1, r9, r10
31+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov s1, r8
32+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov s4, r7
33+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d3, r5, r6
34+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov s5, r4
35+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vldr d4, [sp, #32]
36+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vldr d5, [sp, #40]
37+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vldr d6, [sp, #48]
38+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vldr s14, [sp, #56]
39+
; CHECK-8M-FP-CVE-2021-35465-NEXT: ldr r4, [sp, #64]
40+
; CHECK-8M-FP-CVE-2021-35465-NEXT: bic r4, r4, #159
41+
; CHECK-8M-FP-CVE-2021-35465-NEXT: bic r4, r4, #4026531840
42+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmsr fpscr, r4
43+
; CHECK-8M-FP-CVE-2021-35465-NEXT: msr apsr_nzcvq, r12
44+
; CHECK-8M-FP-CVE-2021-35465-NEXT: blxns r12
45+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r9, r10, d0
46+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d3, [sp, #24]
47+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r7, r8, d1
48+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d4, [sp, #32]
49+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov r5, r6, d2
50+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d5, [sp, #40]
51+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d6, [sp, #48]
52+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vstr d7, [sp, #56]
53+
; CHECK-8M-FP-CVE-2021-35465-NEXT: mrs r11, control
54+
; CHECK-8M-FP-CVE-2021-35465-NEXT: tst.w r11, #8
55+
; CHECK-8M-FP-CVE-2021-35465-NEXT: it ne
56+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmovne.f32 s0, s0
57+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vlldm sp
58+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d0, r9, r10
59+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d1, r7, r8
60+
; CHECK-8M-FP-CVE-2021-35465-NEXT: vmov d2, r5, r6
61+
; CHECK-8M-FP-CVE-2021-35465-NEXT: add sp, #136
62+
; CHECK-8M-FP-CVE-2021-35465-NEXT: pop.w {r4, r5, r6, r7, r8, r9, r10, r11}
63+
; CHECK-8M-FP-CVE-2021-35465-NEXT: pop {r7, pc}
64+
entry:
65+
%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
66+
ret %indirect %res
67+
}
68+
69+
attributes #0 = { "cmse_nonsecure_call" }

0 commit comments

Comments
 (0)