CVE CVE-2021-35465 
VLLDM instruction Security Vulnerability
Disclosure date

23rd August 2021

Arm Cortex-M33 r0p0 to r1p0
Arm Cortex-M35P r0
Arm Cortex-M55 r0p0 to r1p0
Arm China STAR-MC1 (STAR SE configuration)
On these processors, any Armv8-M Secure software running on that utilize FPU or Helium and call Non-secure functions might be affected.
Secure information in floating-point or vector registers can be exposed to the Non-secure world.
CVSS Base Score
3.4 (Low) 
CVSS Vector string


On an Arm Cortex-M-based system, when the floating-point unit (FPU) or M-Profile vector extension (MVE) also known as Helium, is implemented, the processor contains thirty-two 32-bit ‘S’ registers, a Floating-point Status and Control Register (FPSCR), and a Vector Predication Register (VPR, only present with MVE). If the Security Extension (TrustZone) is implemented, Secure software can optionally utilize the FPU or MVE features, in this case these registers might contain Secure information.  
To reduce the software overhead of saving and restoring the Secure context when calling a Non-secure function from Secure state and prevent the inadvertent creation of a floating-point context, the Armv8-M architecture supports a pair of instructions called VLSTM and VLLDM (Floating-point Lazy Store Multiple and Floating-point Lazy Load Multiple). These instructions allow the Secure information to be saved and restored automatically only if the Non-secure function being called and also utilized by FPU or MVE feature. The insertion of VLSTM and VLLDM is normally handled by C/C++ compilers. 
Recently a vulnerability issue was found in the implementation of VLLDM instruction in the Arm Cortex-M33, Cortex-M35P, Cortex-M55, and Arm China STAR-MC1(STAR SE configuration) processors. If the VLLDM instruction is abandoned due to an exception (for example an interrupt) when it is partially completed, it is possible for subsequent Non-secure handler (which may be a tail chained handler from the original exception) to access and modify the partial restored register values. This vulnerability is identified as CVE-2021-35465. Software workaround details added in C/C++ compilers are provided in this document to mitigate this vulnerability issue.


On an Armv8-M-based system with Security Extension implemented, on transition from Secure state to Non-secure state through BLXNS instruction, then the code sequence preceding BLXNS instructions must:

  • Save active non-banked registers by copying them to Secure memory (excluding arguments passing from Secure caller to a Non-secure function).
  • Clear all non-banked registers except:
    • The Link Register (LR)
    • The registers that hold the arguments of the call
    • Registers that do not hold secret information
  • Save and clear all contents in the Floating-point or MVE registers (S0-S31, FPSCR, and VPR), excluding Non-secure function arguments, if they contain Secure data.
  • The branch target address must have the LSB set to 0 and reside in Non-secure memory.

If the Secure software uses the FPU or MVE feature, before transitioning to Non-secure state, it is required that all the Floating-point or MVE registers (S0-S31, FPSCR, and VPR) are protected from the Non-secure function being called (for example by saving the data to secure memory and clearing the registers). Post completing the Non-secure function, on return to Secure state, the data that was in the Floating-point or MVE registers must be unprotected and made available to the secure code (for example by restoring the data from secure memory). To improve the process of protecting and unprotecting Floating-point or MVE registers the VLLDM and VLSTM instructions were introduced. These instructions save and clear the registers only when Floating-point or vector registers are used in Non-secure state.

On an Armv8-M-based system configured with the Security Extension, and FPU or MVE, if Secure software uses the FPU or MVE for Secure data processing, then the general programming practice followed is to:

  • Use VLSTM instruction for code sequence just before transitioning to Non-secure code 
  • Use VLLDM instruction to restore the contents back in Secure code

If any of the Secure software uses FPU or MVE registers and calls a Non-secure function, the VLSTM and VLLDM instruction pair should be used for:

  • Compilations for software components without FPU or MVE usage. It is possible that such software components could link with other software that use FPU or MVE, and so the FP or vector registers could contain Secure information.
    Note: This does not prevent the software component being used on a processor without an FPU or MVE, because the VLSTM and VLLDM instructions behave as a NOP if the FPU or MVE is not present.
  • Compilations with soft-fp ABI (Application Binary Interface)
  • Compilations with hard-fp ABI, and the function call does not have FP or vector arguments or results.

An exception to this arrangement is when hard FP ABI is used, and the function call uses FPU or vector registers for function call argument or result passing.  

Vulnerability issue

When a VLLDM instruction restores the contents of Secure Floating-point registers that were protected by a VLSTM instruction, the VLLDM instructions mark the Floating-point context as active (through CONTROL.FPCA bit) only after restoring all the Secure Floating-point registers contents. The Secure Floating-point context includes FPSCR, VPR, and Floating-point ‘S’ register contents. Now, if a VLLDM instruction has started to restore the Secure data and it is abandoned due to an exception (for example an interrupt) mid-way, the partially restored Secure context will be present in Floating-point registers, when the context is not yet marked active by CONTROL.FPCA bit. Therefore, this effectively means that part of the Secure Floating-point or vector context can be exposed to, and potentially be modified by a subsequent Non-secure handler, resulting in a security vulnerability. 

Products affected

  • Armv8-M architecture
  • Processor hardware: Arm Cortex-M33, Cortex-M55, Cortex-M35P processors. Arm China STAR-MC1 processor (STAR SE configuration) 
  • Software toolchain: Arm Compiler 6 (and LLVM codebase), GCC Arm Embedded toolchain, Fast Model, Cycle Models

Products not affected

  • Example code in DS-5/Arm Development Studio
  • CMSIS Packs
  • Systems that do not support Armv8-M TrustZone technology
  • Secure software that does not use FPU/MVE technology
  • Secure software that does not call Non-secure functions
  • Secure software that only stores Non-secure values in the Floating-point or MVE registers (S0-S31, FPSCR, and VPR)


To mitigate this security vulnerability, you are required to recompile the Secure image with C/C++ compilers that has the software workaround implemented. In the updated C/C++ compilers, instead of executing just the VLLDM instruction after the Non-secure function call, the mitigation code sequence given in Table 1 should be used:

Table 1 - Mitigation Code Sequence

Code sequence after the Non-secure function call
MRS        R5, CONTROL
TST        R5, #8   /* CONTROL_S.SFPA */
IT         NE
VMOVNE S0, S0       /* Note 1 */
VLLDM      SP       /* Lazy restore of d0-d15 and FPSCR. */
vscclrm   {VPR}

Note 1: If the toolchain environment does not permit the use of “vmovne  s0, s0” in a software-float only environment, it might be necessary to insert the binary value for the instruction (0xeeb00a40) instead. In GNU assembly syntax this can be written as:

    .inst.w    0xeeb00a40

The instruction sequences for the fix for this security issue have also been chosen so they are safe regardless of whether the FPU and MVE are implemented.

The example codes for calling a Non-secure function with the software workaround are given in Table 2 and Table 3, with the workaround code sequence highlighted in bold.

Table 2 - Cortex-M55 (Workaround Code Sequence)

Cortex-M55 (Example workaround code sequence)


     2fc:       e92d 0ff0       stmdb   sp!,{r4,r5,r6,r7,r8,r9,sl,fp}
     300:       b0a2            sub     sp,#136;0x88
     302:       ec2d 0a00       vlstm   sp
     306:       e89f 9ff7       clrm    {r0,r1,r2,r4,r5,r6,r7,r8,r9,sl,fp,ip,APSR}
     30a:       479c            blxns   r3
     30c:       ec9f 0b00       vscclrm {VPR}
     310:       ec3d 0a00       vlldm   sp
     314:       b022            add     sp,#136;0x88
     316:       e8bd 0ff0       ldmia.w sp!,{r4,r5,r6,r7,r8,r9,sl,fp}
     31a:       bf00            nop
     31c:       b003            add     sp,#12  
     31e:       f85d fb04       ldr.w   pc,[sp],#4

Table 3 - Cortex-M33, Cortex-M35P, STAR (Workaround Code Sequence)

Cortex-M33, Cortex-M35P, STAR (Example workaround code sequence)


    2680:       e92d 4fe0       stmdb   sp!,{r5,r6,r7,r8,r9,sl,fp,lr}
    2684:       4627            mov     r7,r4
    2686:       46a0            mov     r8,r4
    2688:       46a1            mov     r9,r4
    268a:       46a2            mov     sl,r4
    268c:       46a3            mov     fp,r4
    268e:       46a4            mov     ip,r4
    2690:       b0a2            sub     sp,#136;0x88
    2692:       ec2d 0a00       vlstm   sp
    2696:       f384 8800       msr     CPSR_f,r4
    269a:       4625            mov     r5,r4
    269c:       4626            mov     r6,r4
    269e:       47a4            blxns   r4
    26a0:       f3ef 8514       mrs     r5,CONTROL
    26a4:       f015 0f08       tst.w   r5,#8
    26a8:       bf18            it      ne
    26aa:       eeb0 0a40       vmovne.f32 s0,s0
    26ae:       ec3d 0a00       vlldm   sp
    26b2:       b022            add     sp,#136;0x88
    26b4:       e8bd 8fe0       ldmia.w sp!,{r5,r6,r7,r8,r9,sl,fp,pc}

Mitigation support in compilers

Arm Compiler 6.17

Support for the mitigation has been added to Arm Compiler for Embedded 6.17. The default behavior of the compiler is now as follows:

Target Default 
Armv8-M with the Main Extension
Mitigation disabled
Armv8.1-M with the Main Extension
Mitigation disabled
Mitigation enabled
Mitigation enabled
Mitigation enabled
Mitigation enabled

To enable the mitigation when it is disabled by default, compile with -mfix-cmse-cve-2021-35465.
To disable the mitigation when it is enabled by default, compile with -mno-fix-cmse-cve-2021-35465.
Mitigation should be turned off only when the software being developed is solely for use on devices where the erratum has been fixed in hardware.

GNU compiler

Support for the mitigation has been up-streamed to GCC and is also available in the GNU Arm Embedded Toolchain from version 10.3-2021.10.

The default behavior of the compiler is now as follows:

Target Default 
Armv8-M with the Main Extension
Mitigation in library code
Armv8.1-M with the Main Extension
Mitigation disabled
Mitigation in library code
Mitigation in library code
Mitigation enabled

To enable the mitigation when it is disabled by default, compile with -mfix-cmse-cve-2021-35465.
To disable the mitigation when it is enabled by default, compile with -mno-fix-cmse-cve-2021-35465.
When the mitigation is implemented in library code it cannot be disabled. There is a very small code and performance overhead in this case, but code will continue to execute correctly even on devices that are not subject to the erratum.