This is an archive of the discontinued LLVM Phabricator instance.

[ARM] Mitigate the cve-2021-35465 security vulnurability.
ClosedPublic

Authored by labrinea on Sep 2 2021, 7:30 AM.

Details

Summary

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 https://developer.arm.com/support/arm-security-updates/vlldm-instruction-security-vulnerability

Diff Detail

Event Timeline

labrinea created this revision.Sep 2 2021, 7:30 AM
labrinea requested review of this revision.Sep 2 2021, 7:30 AM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptSep 2 2021, 7:30 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
labrinea edited reviewers, added: chill; removed: momchil.velikov.Sep 3 2021, 2:12 AM
ostannard added inline comments.Sep 6 2021, 4:22 AM
clang/lib/Driver/ToolChains/Clang.cpp
1665 ↗(On Diff #370264)

Are these optional also being passed through to the linker when doing LTO?

clang/test/Driver/arm-cmse-cve-2021-35465.c
4

The last few paragraphs of https://developer.arm.com/support/arm-security-updates/vlldm-instruction-security-vulnerability claim that this is enabled by default for -march=armv8-m.main in AC6 and GCC, is there a reason we're not matching that?

llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
1562

Why are these needed?

1627

This pass runs before the final scheduler pass, so is there a risk that the IT and VMOV instructions could be moved apart? I think it would be safer to either put the IT instruction inside the inline asm block, or make this a new pseudo-instruction expanded in the asm printer.

SjoerdMeijer added inline comments.
clang/lib/Driver/ToolChains/Clang.cpp
1666 ↗(On Diff #370264)

If -mcpu=cortex-[m33|m35|m55] was provided, then -arm-fix-cmse-cve-2021-35465=1 is already set and we are adding another option here? For example, for

-mcpu=cortex-m33 -mcmse -mfix-cmse-cve-2021-35465

I am expecting:

"-mllvm" "-arm-fix-cmse-cve-2021-35465=1"  "-mllvm" "-arm-fix-cmse-cve-2021-35465=1"

and with -mno-fix-cmse-cve-2021-35465:

"-mllvm" "-arm-fix-cmse-cve-2021-35465=1"  "-mllvm" "-arm-fix-cmse-cve-2021-35465=0"

Probably it's nicer to just pass this once.

Also, in the tests, I think cases are missing for -mcpu=... and -m[no-]fix-cmse-cve-2021-35465.

labrinea added inline comments.Sep 6 2021, 6:58 AM
clang/lib/Driver/ToolChains/Clang.cpp
1665 ↗(On Diff #370264)

No, the mitigation is only performed in the compiler. Also, I believe that -flto and -mcmse are incompatible options.

1666 ↗(On Diff #370264)

Your interpretetion is correct. The established policy is "last option wins", but I could make the Driver pass only one option if that's preferable.

clang/test/Driver/arm-cmse-cve-2021-35465.c
4

Yes, the inconsistency lies on the fact that GCC implements the mitigation in library code, therefore it is always present for -march=armv8-m.main, whereas in llvm there's no such limitation. We've contacted the authors of this page to rectify the documentation.

llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
1562

These prevent the reordering with the mitigation sequence. It answers your next question.

1627

The use of .addReg(ARM::CPSR, RegState::ImplicitDefine) prevents the reordering. There are regression tests in place that check the mitigation sequence ordering.

Is this what you meant? Where you refering specifically to the case where !STI->hasFPRegs(), when we generate inline asm, or to both scenarios?

ostannard added inline comments.Sep 6 2021, 7:39 AM
clang/lib/Driver/ToolChains/Clang.cpp
1665 ↗(On Diff #370264)

The mitigation is performed in the backend, which is run from the linker when doing LTO.

Also, I believe that -flto and -mcmse are incompatible options.

Fair enough

1666 ↗(On Diff #370264)

I agree with Sjoerd, the ""last option wins" policy should be implemented in the driver, and only the winning option passed through to CC1. You can use Args.hasFlag instead of getLastArg here, with the default set based on the CPU option.

clang/test/Driver/arm-cmse-cve-2021-35465.c
4

This still sounds like an inconsistency which will catch out users migrating between GCC and clang. I'd prefer that we match GCC's behaviour, though I'd also be OK with leaving it like this as long as these defaults are clearly documented in ClangCommandLineReference.rst.

llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
1562

Do you mean that this is modeling the effect of this instruction on the CONTROL register, to prevent it from being re-ordered with the MRS instruction? If so, then this is unrelated to my next question, and could do with a comment because I wouldn't expect any CONTROL bits to be part of ARM::CPSR (they are different registers).

1627

My concern is that these two instructions (IT and VMOV) have to be adjacent, otherwise the IT would apply to some other instruction, but I don't think there's anything here which guarantees that. I don't think a regression test is enough here, because the re-ordering could happen with a future scheduling model not covered by the tests, or with different code to the tests.

ostannard added inline comments.Sep 6 2021, 7:59 AM
clang/lib/Driver/ToolChains/Clang.cpp
1665 ↗(On Diff #370264)

Actually, I just checked and these options are accepted together, and I can't find any docs saying they are incompatible. Do you have a link to something I've missed? Since there isn't already an error, I think we should either fix this to work with LTO (my preference), or add an error when using the options together, and document that.

labrinea added inline comments.Sep 6 2021, 1:26 PM
clang/lib/Driver/ToolChains/Clang.cpp
1665 ↗(On Diff #370264)

I have addressed all the other comments, but I am not sure how to go about this one. What does it take to make the cve-2021-35465 option work with LTO? Could you elaborate on this?

labrinea updated this revision to Diff 371028.Sep 7 2021, 3:53 AM

Changes in this revision:

  • pass the -arm-fix-cmse-cve-2021-35465 option once
  • document -m(no)fix-cmse-cve-2021-35465 in ClangCommandLineReference.rst
  • add clang tests with the mitigation expicitely disabled on affected cpus
  • removed .addReg(ARM::CPSR, RegState::ImplicitDefine)
  • moved the mitigation sequence inside a bundle to prevent reordering
SjoerdMeijer added inline comments.Sep 7 2021, 4:09 AM
clang/lib/Driver/ToolChains/Clang.cpp
1646 ↗(On Diff #371028)

Nit: capital F in variable name.

1666 ↗(On Diff #371028)

I am wondering if this should use getLastArg and what happens with test cases (which I guess need adding) that have both:

-mno-fix-cmse-cve-2021-35465  -mfix-cmse-cve-2021-35465

or

-mfix-cmse-cve-2021-35465  -mno-fix-cmse-cve-2021-35465
labrinea marked 4 inline comments as done and 6 inline comments as done.Sep 7 2021, 5:49 AM
labrinea added inline comments.
clang/lib/Driver/ToolChains/Clang.cpp
1666 ↗(On Diff #371028)

That's the whole point of getLastArg as far as I understand: for options that can either enable or disable a feature, so that the last one wins. I'll add more tests.

SjoerdMeijer added inline comments.Sep 7 2021, 6:01 AM
clang/lib/Driver/ToolChains/Clang.cpp
1666 ↗(On Diff #371028)

Ah, got confused, it's indeed right there!

labrinea updated this revision to Diff 371083.Sep 7 2021, 8:18 AM

Changes in this revision:

  • renamed fix_cve_2021_35465 to Fix_CVE_2021_35465
  • added more Driver tests to cover the use of -mfix-cmse-cve-2021-35465 with -mno-fix-cmse-cve-2021-35465
  • fixed code indentation

The driver part looks good to me. I will let Oli do the approval as he also looked at the codegen (and I didn't).

clang/include/clang/Driver/Options.td
3284

Nit: I think Arm -> ARM is better as we we're talking about the ARM backend here.

SjoerdMeijer added inline comments.Sep 8 2021, 6:42 AM
llvm/test/CodeGen/ARM/cmse-cve-2021-35465-return.ll
7

As you're creating machine instructions, I think it is better to run this and the other test with -verify-machineinstrs.

@ostannard, can you explain what you meant with supporting LTO? I didn't quite undestand. Are you happy with the rest of the changes?

labrinea updated this revision to Diff 372674.Sep 15 2021, 3:59 AM

Changes in this revision:

  • Replaced the backend option that enables the mitigation with a subtarget feature so that it works with LTO (@lenary thanks for the offline hint)
  • Enabled the subtarget feature on the affected CPUs
labrinea updated this revision to Diff 372677.Sep 15 2021, 4:50 AM

Changes in this revision:

  • added -verify-machineinstrs to the tests
  • that yield two bugs that I had to address:
*** Bad machine code: Explicit operand marked as def ***
- function:    func
- basic block: %bb.0 entry (0x890b6d8)
- instruction: $d3 = VSTRD $sp, 6, 14, $noreg
- operand 0:   $d3
*** Bad machine code: Explicit definition marked as use ***
- function:    non_secure_call
- basic block: %bb.0  (0x8e0bed8)
- instruction: t2MRS_M $r12, 20, 14, $noreg
- operand 0:   $r12
lenary accepted this revision.Sep 15 2021, 5:12 AM

LGTM, but the most recent way of implementing this (using target features) was something I suggested to Alexandros based on @ostannard's feedback about LTO. I think it is cleaner, and this patch is good, but a re-review from others would be helpful.

This revision is now accepted and ready to land.Sep 15 2021, 5:12 AM
This revision was landed with ongoing or failed builds.Sep 16 2021, 5:16 AM
This revision was automatically updated to reflect the committed changes.