Fix portSWITCH_TO_USER_MODE() on Armv7-M MPU ports (#803)

A task's privilege level is stored in ulTaskFlag member in the TCB. Current
implementation of portSWITCH_TO_USER_MODE() does not update this
flag but just lowers the processor's privilege level. This results in many
APIs incorrectly determining task's privilege level and access permissions -

- xPortIsAuthorizedToAccessBuffer
- xPortIsTaskPrivileged
- xPortIsAuthorizedToAccessKernelObject

This PR fixes the portSWITCH_TO_USER_MODE() implementation to correctly
update the ulTaskFlag member in the TCB before lowering the processor's
privilege level.
This commit is contained in:
Soren Ptak 2023-09-26 02:06:23 -07:00 committed by GitHub
parent ac5deb155d
commit 84bdb05bd2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 131 additions and 45 deletions

View file

@ -185,6 +185,11 @@ BaseType_t xIsPrivileged( void ) __attribute__( ( naked ) );
*/
void vResetPrivilege( void ) __attribute__( ( naked ) );
/**
* @brief Make a task unprivileged.
*/
void vPortSwitchToUserMode( void );
/**
* @brief Enter critical section.
*/
@ -284,7 +289,7 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
}
else
{
xMPUSettings->ulTaskFlags &= ( ~portTASK_IS_PRIVILEGED_FLAG );
xMPUSettings->ulTaskFlags &= ( ~( portTASK_IS_PRIVILEGED_FLAG ) );
xMPUSettings->ulContext[ 0 ] = portINITIAL_CONTROL_IF_UNPRIVILEGED;
}
@ -1209,6 +1214,19 @@ void vResetPrivilege( void ) /* __attribute__ (( naked )) */
}
/*-----------------------------------------------------------*/
void vPortSwitchToUserMode( void )
{
/* Load the current task's MPU settings from its TCB. */
xMPU_SETTINGS * xTaskMpuSettings = xTaskGetMPUSettings( NULL );
/* Mark the task as unprivileged. */
xTaskMpuSettings->ulTaskFlags &= ( ~( portTASK_IS_PRIVILEGED_FLAG ) );
/* Lower the processor's privilege level. */
vResetPrivilege();
}
/*-----------------------------------------------------------*/
void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
const struct xMEMORY_REGION * const xRegions,
StackType_t * pxBottomOfStack,

View file

@ -96,8 +96,6 @@ typedef unsigned long UBaseType_t;
#define portNUM_CONFIGURABLE_REGIONS ( ( portLAST_CONFIGURABLE_REGION - portFIRST_CONFIGURABLE_REGION ) + 1 )
#define portTOTAL_NUM_REGIONS_IN_TCB ( portNUM_CONFIGURABLE_REGIONS + 1 ) /* Plus one to make space for the stack region. */
#define portSWITCH_TO_USER_MODE() __asm volatile ( " mrs r0, control \n orr r0, #1 \n msr control, r0 " ::: "r0", "memory" )
typedef struct MPU_REGION_REGISTERS
{
uint32_t ulRegionBaseAddress;
@ -268,6 +266,7 @@ extern void vPortExitCritical( void );
extern BaseType_t xIsPrivileged( void );
extern void vResetPrivilege( void );
extern void vPortSwitchToUserMode( void );
/**
* @brief Checks whether or not the processor is privileged.
@ -286,6 +285,14 @@ extern void vResetPrivilege( void );
* register.
*/
#define portRESET_PRIVILEGE() vResetPrivilege()
/**
* @brief Make a task unprivileged.
*
* It must be called from privileged tasks only. Calling it from unprivileged
* task will result in a memory protection fault.
*/
#define portSWITCH_TO_USER_MODE() vPortSwitchToUserMode()
/*-----------------------------------------------------------*/
extern BaseType_t xPortIsTaskPrivileged( void );

View file

@ -206,6 +206,11 @@ BaseType_t xIsPrivileged( void ) __attribute__( ( naked ) );
*/
void vResetPrivilege( void ) __attribute__( ( naked ) );
/**
* @brief Make a task unprivileged.
*/
void vPortSwitchToUserMode( void );
/**
* @brief Enter critical section.
*/
@ -312,7 +317,7 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
}
else
{
xMPUSettings->ulTaskFlags &= ( ~portTASK_IS_PRIVILEGED_FLAG );
xMPUSettings->ulTaskFlags &= ( ~( portTASK_IS_PRIVILEGED_FLAG ) );
xMPUSettings->ulContext[ 0 ] = portINITIAL_CONTROL_IF_UNPRIVILEGED;
}
@ -1390,6 +1395,19 @@ void vResetPrivilege( void ) /* __attribute__ (( naked )) */
}
/*-----------------------------------------------------------*/
void vPortSwitchToUserMode( void )
{
/* Load the current task's MPU settings from its TCB. */
xMPU_SETTINGS * xTaskMpuSettings = xTaskGetMPUSettings( NULL );
/* Mark the task as unprivileged. */
xTaskMpuSettings->ulTaskFlags &= ( ~( portTASK_IS_PRIVILEGED_FLAG ) );
/* Lower the processor's privilege level. */
vResetPrivilege();
}
/*-----------------------------------------------------------*/
void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
const struct xMEMORY_REGION * const xRegions,
StackType_t * pxBottomOfStack,

View file

@ -190,8 +190,6 @@ typedef unsigned long UBaseType_t;
#define portNUM_CONFIGURABLE_REGIONS ( configTOTAL_MPU_REGIONS - 5UL )
#define portTOTAL_NUM_REGIONS_IN_TCB ( portNUM_CONFIGURABLE_REGIONS + 1 ) /* Plus 1 to create space for the stack region. */
#define portSWITCH_TO_USER_MODE() __asm volatile ( " mrs r0, control \n orr r0, #1 \n msr control, r0 " ::: "r0", "memory" )
typedef struct MPU_REGION_REGISTERS
{
uint32_t ulRegionBaseAddress;
@ -362,6 +360,7 @@ extern void vPortExitCritical( void );
extern BaseType_t xIsPrivileged( void );
extern void vResetPrivilege( void );
extern void vPortSwitchToUserMode( void );
/**
* @brief Checks whether or not the processor is privileged.
@ -380,6 +379,14 @@ extern void vResetPrivilege( void );
* register.
*/
#define portRESET_PRIVILEGE() vResetPrivilege()
/**
* @brief Make a task unprivileged.
*
* It must be called from privileged tasks only. Calling it from unprivileged
* task will result in a memory protection fault.
*/
#define portSWITCH_TO_USER_MODE() vPortSwitchToUserMode()
/*-----------------------------------------------------------*/
extern BaseType_t xPortIsTaskPrivileged( void );

View file

@ -283,6 +283,11 @@ extern void vPortRestoreContextOfFirstTask( void ) PRIVILEGED_FUNCTION;
*/
BaseType_t xPortIsTaskPrivileged( void ) PRIVILEGED_FUNCTION;
/**
* @brief Make a task unprivileged.
*/
void vPortSwitchToUserMode( void );
/*-----------------------------------------------------------*/
/* Each task maintains its own interrupt status in the critical nesting
@ -318,7 +323,7 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
}
else
{
xMPUSettings->ulTaskFlags &= ( ~portTASK_IS_PRIVILEGED_FLAG );
xMPUSettings->ulTaskFlags &= ( ~( portTASK_IS_PRIVILEGED_FLAG ) );
xMPUSettings->ulContext[ 0 ] = portINITIAL_CONTROL_IF_UNPRIVILEGED;
}
@ -741,6 +746,19 @@ BaseType_t xPortIsTaskPrivileged( void ) /* PRIVILEGED_FUNCTION */
}
/*-----------------------------------------------------------*/
void vPortSwitchToUserMode( void )
{
/* Load the current task's MPU settings from its TCB. */
xMPU_SETTINGS * xTaskMpuSettings = xTaskGetMPUSettings( NULL );
/* Mark the task as unprivileged. */
xTaskMpuSettings->ulTaskFlags &= ( ~( portTASK_IS_PRIVILEGED_FLAG ) );
/* Lower the processor's privilege level. */
vResetPrivilege();
}
/*-----------------------------------------------------------*/
/*
* See header file for description.
*/

View file

@ -192,8 +192,6 @@ typedef unsigned long UBaseType_t;
#define portNUM_CONFIGURABLE_REGIONS ( configTOTAL_MPU_REGIONS - 5UL )
#define portTOTAL_NUM_REGIONS_IN_TCB ( portNUM_CONFIGURABLE_REGIONS + 1 ) /* Plus 1 to create space for the stack region. */
#define portSWITCH_TO_USER_MODE() __asm volatile ( " mrs r0, control \n orr r0, r0, #1 \n msr control, r0 " ::: "r0", "memory" )
typedef struct MPU_REGION_REGISTERS
{
uint32_t ulRegionBaseAddress;
@ -390,6 +388,7 @@ portFORCE_INLINE static BaseType_t xPortIsInsideInterrupt( void )
extern BaseType_t xIsPrivileged( void );
extern void vResetPrivilege( void );
extern void vPortSwitchToUserMode( void );
/**
* @brief Checks whether or not the processor is privileged.
@ -408,6 +407,14 @@ extern void vResetPrivilege( void );
* register.
*/
#define portRESET_PRIVILEGE() vResetPrivilege()
/**
* @brief Make a task unprivileged.
*
* It must be called from privileged tasks only. Calling it from unprivileged
* task will result in a memory protection fault.
*/
#define portSWITCH_TO_USER_MODE() vPortSwitchToUserMode()
/*-----------------------------------------------------------*/
extern BaseType_t xPortIsTaskPrivileged( void );

View file

@ -219,6 +219,11 @@ BaseType_t xIsPrivileged( void );
*/
void vResetPrivilege( void );
/**
* @brief Make a task unprivileged.
*/
void vPortSwitchToUserMode( void );
/**
* @brief Enter critical section.
*/
@ -312,7 +317,7 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
}
else
{
xMPUSettings->ulTaskFlags &= ( ~portTASK_IS_PRIVILEGED_FLAG );
xMPUSettings->ulTaskFlags &= ( ~( portTASK_IS_PRIVILEGED_FLAG ) );
xMPUSettings->ulContext[ 0 ] = portINITIAL_CONTROL_IF_UNPRIVILEGED;
}
@ -1219,19 +1224,6 @@ __weak void vSetupTimerInterrupt( void )
}
/*-----------------------------------------------------------*/
__asm void vPortSwitchToUserMode( void )
{
/* *INDENT-OFF* */
PRESERVE8
mrs r0, control
orr r0, #1
msr control, r0
bx r14
/* *INDENT-ON* */
}
/*-----------------------------------------------------------*/
__asm void vPortEnableVFP( void )
{
/* *INDENT-OFF* */
@ -1371,6 +1363,19 @@ __asm void vResetPrivilege( void )
}
/*-----------------------------------------------------------*/
void vPortSwitchToUserMode( void )
{
/* Load the current task's MPU settings from its TCB. */
xMPU_SETTINGS * xTaskMpuSettings = xTaskGetMPUSettings( NULL );
/* Mark the task as unprivileged. */
xTaskMpuSettings->ulTaskFlags &= ( ~( portTASK_IS_PRIVILEGED_FLAG ) );
/* Lower the processor's privilege level. */
vResetPrivilege();
}
/*-----------------------------------------------------------*/
void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
const struct xMEMORY_REGION * const xRegions,
StackType_t * pxBottomOfStack,

View file

@ -189,9 +189,6 @@ typedef unsigned long UBaseType_t;
#define portNUM_CONFIGURABLE_REGIONS ( configTOTAL_MPU_REGIONS - 5UL )
#define portTOTAL_NUM_REGIONS_IN_TCB ( portNUM_CONFIGURABLE_REGIONS + 1 ) /* Plus 1 to create space for the stack region. */
void vPortSwitchToUserMode( void );
#define portSWITCH_TO_USER_MODE() vPortSwitchToUserMode()
typedef struct MPU_REGION_REGISTERS
{
uint32_t ulRegionBaseAddress;
@ -356,6 +353,7 @@ extern void vPortExitCritical( void );
extern BaseType_t xIsPrivileged( void );
extern void vResetPrivilege( void );
extern void vPortSwitchToUserMode( void );
/**
* @brief Checks whether or not the processor is privileged.
@ -374,6 +372,14 @@ extern void vResetPrivilege( void );
* register.
*/
#define portRESET_PRIVILEGE() vResetPrivilege()
/**
* @brief Make a task unprivileged.
*
* It must be called from privileged tasks only. Calling it from unprivileged
* task will result in a memory protection fault.
*/
#define portSWITCH_TO_USER_MODE() vPortSwitchToUserMode()
/*-----------------------------------------------------------*/
extern BaseType_t xPortIsTaskPrivileged( void );