CM33 with ARMv8 MPU (critical sections, xPortIsTaskPrivileged)

Hi, I am using the MPU for a CM33 system and have some privileged and unprivileged tasks.

I have a dependency on a third-party library component for interacting with some hardware. This library needs to be able to guard several operations in a critical section, but I would really like to keep the task that calls these library functions unprivileged. I see that some other ports (ARMv7) include the config option configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS and that vPortEnterCritical and vPortExitCritical are moved to the freertos_system_calls section from privileged_functions if that config is enabled. Could this config option be brought over for the ARMv8 CM33 port? I am happy to submit a PR doing so.

I’ve seen previous discussion about this on this forum- (The post url ends with “cortex-m33-and-mpu/17581/5” - sorry, new users aren’t allowed to post links), but I think for my use-case the pros of keeping the task unprivileged are higher than the cons of the restricted task blocking the timer. Maybe some middle ground could be a compile-time warning that mentions the risk of allowing unprivileged critical sections?

Additionally, I would really like to call xPortIsTaskPrivileged to assert that my unprivileged tasks are actually unprivileged. Main concern is that unprivileged to privileged is only decided by a single bit which is very weak against any kind of targeted hardware attacks(fault injection). I don’t see a good reason why unprivileged tasks should not be able to view their privilege levels, and would argue that being able to assert on privilege level is a security improvement. Happy to submit a PR for this change as well.

This seems like a good idea. You’ll probably get a response from someone on the FreeRTOS team here too regarding whether you should go ahead with the PR.

Asserting on privilege seems like another good idea. However just for clarity that is not precisely the same thing as allowing tasks to view their privilege levels. One of the tenets of software security is not to allow software to determine that it is privileged. Would a “assert-if-privileged” function be sufficient for your purposes?

1 Like

Thank you for the response!

I think the functionality I want is “assert-task-is-unprivileged”, and as far as I can tell, this would not allow software to determine that it is privileged (since that assert would fail).

My use-case would be a task that should be unprivileged but starts off privileged to set up some initial state. After it finishes initialization, it then attempts to drop privileges and then I want to assert that it is actually unprivileged

This flag exists in those ports only for backward compatibility and is not recommended to use in a security focused application. An unprivileged task can enter a critical section and never exit, thereby halting the complete system.

Why would you like to add that assert? Have you seen our latest version which includes a new MPU wrapper with several security focused enhancements - We have also published a threat model - Kernel Threat Model - FreeRTOS.

1 Like

So you want to test that the API vResetPrivilege works as expected? The reliable way to do that is to an operation that should generate a memory fault and then gracefully recover from that fault. Many of our examples show how to do that - The reason asserting on xPortIsTaskPrivileged is not a good idea is because if you think that vResetPrivilege can not work as expected, then by the same argument, xPortIsTaskPrivileged may not work as expected too.

1 Like

Could a watchdog interrupt that does not get masked during a critical section be a good solution here?

My concern is a third-party library that I want to be unprivileged because it is complex and the code paths can be hit with untrusted user input. This library also interacts with hardware and needs to enter/exit critical sections.

I think (for my use case) the downsides of this task being privileged outweigh the downsides of the system being halted. Even more so if I can detect the system being halted with the watchdog interrupt.

For time-critical systems I understand this is not the case, which is why this can potentially be a config option with a compile-time warning of the risk being introduced

I’ve seen the threat model, but it explicitly mentions that 4. Physical Attacks are up to the application writer to mitigate against. Being able to call xPortIsTaskPrivileged will help me mitigate against physical attacks targeting vResetPrivilege .

In the below blog post, they describe that it is easy to skip a single instruction but difficult for an attacker to skip multiple consecutive instructions. See Skipping of instructions table

The concern is not that the functions don’t work as expected, but that they are skipped over by an attacker. Asserting on xPortIsTaskPrivileged can be done multiple times in a row (which is difficult for an attacker to skip past all of these), and this would help give me confidence that an attacker did not successfully skip over the calls to vResetPrivilege or xPortIsTaskPrivileged.

Is it not possible to achieve the same by executing vResetPrivilege multiple times in a row?

If vResetPrivilege succeeds and does not error when the task is unprivileged to begin with then this could work. I did not want to make that assumption and it seemed nice to be able to crash/reset if the unprivileged check failed

I appreciate the feedback.

For systems that do not have DOS/system halting in their threat model, I do like the option of having unprivileged critical sections. I can keep this as a local diff if you are strongly opposed to it:

I was not able to find many(any?) examples of an MPU configuration that explicitly granted unprivileged access to peripherals. I got it working after some trial-and-error, but some peripheral accesses (such as UART TX) will need to enter a critical section.

It would be nice if UART/USART tx/rx could be unprivileged because otherwise we need to be concerned with if the src buffer address is something the task should actually have access to for TX and if the recv buffer is something the task should be able to overwrite for RX. Keeping this task unprivileged makes this very simple

The problem with this is that we can no longer claim that an unprivileged task will not be able to break its boundary and impact other tasks.

Using mutex does not work?

I agree and that is the intent. I am just trying to see if it is possible to do without introducing a non-secure option. If not, we can think about adding it as a option which is turned-off by default and gives a warning when set.

Unless I am out of date, I think the usual solution is to keep the transmit and receive buffers private to the UART Tx/Rx driver. The API that copies data between that memory and memory owned by the unprivileged calling task must check that the buffer addresses supplied by the calling task are permitted in the context of that task.