Tuesday, 6 May 2025

Switching Privilege Levels on ARM Cortex-M: Using the CONTROL Register for Secure Thread Mode

In this article, we’ll walk through how the ARM Cortex-M architecture enforces two privilege levels in Thread mode, demonstrate dropping Thread mode to unprivileged via inline assembly (MRS/MSR on the CONTROL register), trigger and observe the resulting UsageFault when unprivileged code attempts protected accesses, and explain how to regain privilege only via exception (Handler) mode. Finally, we’ll discuss why this mechanism underpins RTOS task isolation and security.

Thread vs. Handler Mode Privilege Levels

ARM Cortex-M cores distinguish between two execution contexts:

  • Thread Mode: Where application or RTOS tasks run; can be Privileged or Unprivileged based on CONTROL.nPRIV (bit 0). 

  • Handler Mode: Always Privileged, entered on exception or interrupt.

By default after reset, Thread mode is Privileged. Only Handler mode may clear the nPRIV bit to restore Thread mode’s Privileged level.

Reading and Writing the CONTROL Register

The CONTROL register’s nPRIV bit controls Thread-mode privilege:

  • Reading: MRS R0, CONTROL moves CONTROL into R0.

  • Modifying: Perform a read-modify-write sequence:

    __asm volatile ( "MRS R0, CONTROL\n" "ORR R0, R0, #1 \n" /* set nPRIV */ "MSR CONTROL, R0 \n" :::"r0" );

    MSR CONTROL, R0 preserves Handler-mode privilege requirement—unprivileged Thread code cannot write CONTROL directly.

Demonstration: Dropping to Unprivileged Thread Mode

  1. Start in Thread mode, Privileged (CONTROL = 0).

  2. Call drop_to_unprivileged() implementing the MRS/ORR/MSR sequence above.

  3. CONTROL becomes 1; Thread mode is now Unprivileged.

  4. Attempt to write an NVIC register (only Privileged):

    NVIC->ISPR[0] |= (1U << irq);
  5. Result: Cortex-M triggers a UsageFault because Unprivileged code cannot modify system registers.

Observing the UsageFault

On hardware or in the debugger:

  • Execution jumps to the HardFault handler after the UsageFault escalates.

  • Fault analyzer shows a UsageFault origin—indicating restricted access by Unprivileged Thread code.

Regaining Privilege: Exception Entry Path

Unprivileged Thread code cannot directly set CONTROL.nPRIV back to 0. The only path is:

  1. Generate an exception (e.g., via SVCall or another IRQ).

  2. On entry, core switches to Handler mode (always Privileged).

  3. In Handler, use MSR CONTROL, <value> to clear nPRIV.

  4. Return from exception, dropping back to Thread mode now Privileged.

RTOS Task Isolation Use Case

Real-time kernels (e.g., FreeRTOS) leverage this hardware feature to sandbox user tasks:

  • Kernel runs at Privileged Thread mode to manage resources.

  • User tasks are launched as Unprivileged Thread mode, preventing direct access to critical control registers or disabling interrupts.

  • System calls (SVCall) elevate to Handler mode, letting the kernel on behalf of the user task perform privileged operations.

Conclusion

ARM Cortex-M’s dual privilege levels in Thread mode and mandatory Handler-mode privilege provide a lightweight, hardware-enforced sandbox. By mastering MRS/MSR access to the CONTROL register and the exception-based path back to Privileged mode, embedded developers can build robust, secure firmware and RTOS task isolation. This mechanism is fundamental for preventing errant or malicious user code from compromising system stability or security.

Written By: Musaab Taha


This article was improved with the assistance of AI.

No comments:

Post a Comment