-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TaskYield implementation for Cortex-M33 NTZ is incorrect #788
Comments
Hey @alejoseb, thanks for filing this bug. You're saying that when using the ARM_CM33_NTZ/non_secure port that an unprivileged task would trigger a data abort when attempting to access the portNVIC_INT_CTRL_REG inside of the vPortYield() function?
Per Arm's documentation, it is true that attempting to access this register in unprivileged mode would lead to an abort. However, the vPortYield() function is marked as a PRIVILEGED_FUNCTION:
When using an MPU port with unprivileged tasks these
Then before starting the FreeRTOS Scheduler an MPU region is set that enforces protections on this flash section such that unprivileged tasks can not access it. This is done in prvSetupDefaultMPU():863
Where putting this all together, your unprivileged tasks shouldn't be hitting a memory abort when trying to access portNVIC_INT_CTRL_REG inside of Your tasks should be calling functions such as vTaskDelay() or another blocking API(), such as waiting for a mutex or an event group, to inform the FreeRTOS-Scheduler that they should be swapped out for another task. Where the FreeRTOS-Kernel will then, while in a privileged operating mode, use this function to swap tasks. Does that explain why the behaviour you are seeing isn't a bug, but actually intended? |
Hi @Skptak, Notice that vTaksDelay is implemented with mpu_wrappers, i.e., before executing the actual delay, the privilege will be elevated within the MPU_vTaskDelay wrapper which later calls the vTaksDelay on privileged level and then drops privileges after returning from the delay call. In the case of vPortYield there is no MPU wrapper it is called directly, therefore the code within this function is executed in unprivileged mode regardless of being in PRIVILEGED_FUNCTIONS memory section. Allocating code in the PRIVILEGED_FUNCTIONS section does not elevate privileges. Therefore this line: will trigger an exception, since it is attempting to write a register ( portNVIC_INT_CTRL_REG ) that requires privileged level. The register that is being accessed is detailed here: https://developer.arm.com/documentation/100235/0004/the-cortex-m33-peripherals/system-control-block/interrupt-control-and-state-register?lang=en |
That's actually exactly my point, an unprivileged FreeRTOS Task does not have the ability to directly call
As described in the FreeRTOS ARMv8M Architecture documentation you mentioned, Using FreeRTOS with Memory Protection Unit (MPU) Support, memory layout section.
This same wrapping is essentially what is happening with In the Armv7-M ports you mentioned in your original post this function is guarded inside of the FreeRTOS_SVC_Handler itself:
Where if compiling the port WITHOUT configENFORE_SYSTEM_CALLS_FROM_KERNEL_ONLY set to 1 we generate a compiler warning say we do not recommend that
|
What is the reasoning for preventing calling vPortYield() from an unprivileged task? What are the security implications? Yielding the execution of a task is a basic primitive of any RTOS. It simply lets know the kernel that the task has nothing to do at that moment and other tasks that are ready can be executed. Calling it only as part of another function doesn't make sense. Also, the idea is not to disable configENFORE_SYSTEM_CALLS_FROM_KERNEL_ONLY. The idea is to have a proper syscall for ARMv7 and Armv8. If disabling that control is needed, then that implies that ARMv7 is also missing a proper syscall. Also, If portyield should not be accessible from user space code then that function should be static. Currently it is not static and is accessible from user space. A work around could be to call the delay function with 0 as a parameter. However this will reduce portability needing different source code when executing in privileged and unprivileged mode. This is a relevant discussion about this topic: https://www.freertos.org/FreeRTOS_Support_Forum_Archive/March_2011/freertos_vTaskDelay_0_vs_taskYIELD_4401035.html |
Hi @alejoseb |
@alejoseb In my opinion, this is not a bug. The whole reason we have MPU regions and memory protection is because it provides the ability for the application programmer to choose whether they want protection for their tasks or not. If you'd like to have MPU protection for your task, simply make it privileged. Privileged tasks are not meant to be reserved for the FreeRTOS Kernel. Otherwise, we are not preventing you from calling yield; we are simply providing a wrapper so that your yield call (or, in this case, |
@alejoseb If you'd like to discuss more, please feel free to open a question on the FreeRTOS.org forum. (If you've already done so, I do apologize for the duplicate instructions.) |
@ydhuang28 Sorry, but this statement is wrong If you make a task privileged then this task is not protected at all. In fact, a privileged task can access any memory region (flash, ram, peripherals) but the kernel. So, if you have a bug or vulnerability in your privileged task, then that will not be contained and potentially affect other memory areas. This issue is not security related so I didn't report directly to AWS. I understand that you can call the delay function with (0) as a parameter and avoid having a proper syscall for the taskyield() function. As I mentioned earlier this reduces code portability, since you will need different code bases to run privileged and unprivileged. There is no reason to continue the discussion in the forum, but it is better to clarify the right usage of the MPU, the privileged and unprivileged mode. |
@aggarg thanks the PR solves the issue. |
Describe the bug
TaskYield implementation is wrong for the Cortex-M33 NTZ port when using MPU. Currently, it only works if the restricted task is executed with privileged level. The reason is that TaskYield requires writing the control register that is only writable in the privileged mode:
#define portNVIC_INT_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000ed04 ) )
void vPortYield( void ) /* PRIVILEGED_FUNCTION /
{
/ Set a PendSV to request a context switch. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; // -> BUG this needs privileged level to be written
}
Target
Host
To Reproduce
Create a project with the port for Cortex-M33 NTZ with MPU enabled.
using the following configuration
#define configENABLE_MPU 1
#define configENABLE_TRUSTZONE 0
Create a restricted unprivileged task, and within the task call taskYIELD();
This will trigger a memfaullt exception.
Expected behavior
The task yields or forces a context switch
Screenshots
If applicable, add screenshots to help explain your problem.
Additional context
The port for ARMv7 uses the SVC interface instead of the PendSV.
That could be a method to solve the issue.
Also, implementing taskYIELD(); as a syscall can solve the issue, since the execution
of this function needs privileged level to write to portNVIC_INT_CTRL_REG .
The text was updated successfully, but these errors were encountered: