From b4d10fb16e0231b22b76189f99d8c06e7c227901 Mon Sep 17 00:00:00 2001 From: Corentin Pane Date: Thu, 20 Aug 2026 10:10:54 +0200 Subject: [PATCH] Mask MPU region base addresses when writing to MPU_RBAR Base addresses should be aligned to a power of two for proper MPU configuration. Masking ensures that a misaligned base address cannot modify less significant bits in the attribute reserved for other use, such as the region bits. Failing to mask the address may allow a malicious user to pass in misaligned addresses in a user-defined region or as stack buffer which could in turn override the settings for higher-priority kernel-defined regions. --- portable/GCC/ARM_CM3_MPU/port.c | 76 ++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/portable/GCC/ARM_CM3_MPU/port.c b/portable/GCC/ARM_CM3_MPU/port.c index 96d781d969..a32e77690d 100644 --- a/portable/GCC/ARM_CM3_MPU/port.c +++ b/portable/GCC/ARM_CM3_MPU/port.c @@ -157,11 +157,14 @@ typedef void ( * portISR_t )( void ); static void prvSetupMPU( void ) PRIVILEGED_FUNCTION; /* - * Return the smallest MPU region size that a given number of bytes will fit + * Calculate the smallest MPU region size that a given number of bytes will fit * into. The region size is returned as the value that should be programmed - * into the region attribute register for that region. + * into the region attribute register for that region, and as a mask that + * should be applied to the base address to ensure it is aligned with its size. */ -static uint32_t prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes ) PRIVILEGED_FUNCTION; +static void prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes, + uint32_t * ulRegionSizeSetting, + uint32_t * ulBaseAddressMask ) PRIVILEGED_FUNCTION; /* * Setup the timer to generate the tick interrupts. The implementation in this @@ -1135,47 +1138,61 @@ static void prvSetupMPU( void ) /* Check the expected MPU is present. */ if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE ) { + uint32_t ulRegionSizeSetting, ulBaseAddressMask; + /* First setup the unprivileged flash for unprivileged read only access. */ - portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ + prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__, + &( ulRegionSizeSetting ), + &( ulBaseAddressMask ) ); + portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ & ulBaseAddressMask ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portUNPRIVILEGED_FLASH_REGION ); portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_READ_ONLY ) | ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | - ( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | + ( ulRegionSizeSetting ) | ( portMPU_REGION_ENABLE ); /* Setup the privileged flash for privileged only access. This is where * the kernel code is * placed. */ - portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */ + prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__, + &( ulRegionSizeSetting ), + &( ulBaseAddressMask ) ); + portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ & ulBaseAddressMask ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portPRIVILEGED_FLASH_REGION ); portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) | ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | - ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) | + ( ulRegionSizeSetting ) | ( portMPU_REGION_ENABLE ); /* Setup the privileged data RAM region. This is where the kernel data * is placed. */ - portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_data_start__ ) | /* Base address. */ + prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_data_end__ - ( uint32_t ) __privileged_data_start__, + &( ulRegionSizeSetting ), + &( ulBaseAddressMask ) ); + portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_data_start__ & ulBaseAddressMask ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portPRIVILEGED_RAM_REGION ); portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_WRITE ) | ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | ( portMPU_REGION_EXECUTE_NEVER ) | - prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_data_end__ - ( uint32_t ) __privileged_data_start__ ) | + ( ulRegionSizeSetting ) | ( portMPU_REGION_ENABLE ); /* By default allow everything to access the general peripherals. The * system peripherals and registers are protected. */ - portMPU_REGION_BASE_ADDRESS_REG = ( portPERIPHERALS_START_ADDRESS ) | + prvGetMPURegionSizeSetting( portPERIPHERALS_END_ADDRESS - portPERIPHERALS_START_ADDRESS, + &( ulRegionSizeSetting ), + &( ulBaseAddressMask ) ); + portMPU_REGION_BASE_ADDRESS_REG = ( portPERIPHERALS_START_ADDRESS & ulBaseAddressMask ) | ( portMPU_REGION_VALID ) | ( portGENERAL_PERIPHERALS_REGION ); portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER ) | - ( prvGetMPURegionSizeSetting( portPERIPHERALS_END_ADDRESS - portPERIPHERALS_START_ADDRESS ) ) | + ( ulRegionSizeSetting ) | ( portMPU_REGION_ENABLE ); /* Enable the memory fault exception. */ @@ -1187,7 +1204,9 @@ static void prvSetupMPU( void ) } /*-----------------------------------------------------------*/ -static uint32_t prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes ) +static void prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes, + uint32_t * ulRegionSizeSetting, + uint32_t * ulBaseAddressMask ) { uint32_t ulRegionSize, ulReturnValue = 4; @@ -1205,9 +1224,12 @@ static uint32_t prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes ) } } - /* Shift the code by one before returning so it can be written directly + /* Shift the code by one so it can be written directly * into the correct bit position of the attribute register. */ - return( ulReturnValue << 1UL ); + *ulRegionSizeSetting = ulReturnValue << 1UL; + + /* Prepare base address mask to ensure alignment */ + *ulBaseAddressMask = ~( ulRegionSize - 1UL ); } /*-----------------------------------------------------------*/ @@ -1279,16 +1301,20 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings, if( xRegions == NULL ) { /* No MPU regions are specified so allow access to all RAM. */ + uint32_t ulRegionSizeSetting, ulBaseAddressMask; + prvGetMPURegionSizeSetting( ( uint32_t ) __SRAM_segment_end__ - ( uint32_t ) __SRAM_segment_start__, + &( ulRegionSizeSetting ), + &( ulBaseAddressMask ) ); xMPUSettings->xRegion[ 0 ].ulRegionBaseAddress = - ( ( uint32_t ) __SRAM_segment_start__ ) | /* Base address. */ + ( ( uint32_t ) __SRAM_segment_start__ & ulBaseAddressMask ) | /* Base address. */ ( portMPU_REGION_VALID ) | - ( portSTACK_REGION ); /* Region number. */ + ( portSTACK_REGION ); /* Region number. */ xMPUSettings->xRegion[ 0 ].ulRegionAttribute = ( portMPU_REGION_READ_WRITE ) | ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | ( portMPU_REGION_EXECUTE_NEVER ) | - ( prvGetMPURegionSizeSetting( ( uint32_t ) __SRAM_segment_end__ - ( uint32_t ) __SRAM_segment_start__ ) ) | + ( ulRegionSizeSetting ) | ( portMPU_REGION_ENABLE ); xMPUSettings->xRegionSettings[ 0 ].ulRegionStartAddress = ( uint32_t ) __SRAM_segment_start__; @@ -1315,15 +1341,19 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings, if( uxStackDepth > 0 ) { /* Define the region that allows access to the stack. */ + uint32_t ulRegionSizeSetting, ulBaseAddressMask; + prvGetMPURegionSizeSetting( ( uint32_t ) ( uxStackDepth * ( configSTACK_DEPTH_TYPE ) sizeof( StackType_t ) ), + &( ulRegionSizeSetting ), + &( ulBaseAddressMask ) ); xMPUSettings->xRegion[ 0 ].ulRegionBaseAddress = - ( ( uint32_t ) pxBottomOfStack ) | + ( ( uint32_t ) pxBottomOfStack & ulBaseAddressMask ) | ( portMPU_REGION_VALID ) | ( portSTACK_REGION ); /* Region number. */ xMPUSettings->xRegion[ 0 ].ulRegionAttribute = ( portMPU_REGION_READ_WRITE ) | ( portMPU_REGION_EXECUTE_NEVER ) | - ( prvGetMPURegionSizeSetting ( ( uint32_t ) ( uxStackDepth * ( configSTACK_DEPTH_TYPE ) sizeof( StackType_t ) ) ) ) | + ( ulRegionSizeSetting ) | ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | ( portMPU_REGION_ENABLE ); xMPUSettings->xRegionSettings[ 0 ].ulRegionStartAddress = ( uint32_t ) pxBottomOfStack; @@ -1342,13 +1372,17 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings, /* Translate the generic region definition contained in * xRegions into the CM3 specific MPU settings that are then * stored in xMPUSettings. */ + uint32_t ulRegionSizeSetting, ulBaseAddressMask; + prvGetMPURegionSizeSetting( xRegions[ lIndex ].ulLengthInBytes, + &( ulRegionSizeSetting ), + &( ulBaseAddressMask ) ); xMPUSettings->xRegion[ ul ].ulRegionBaseAddress = - ( ( uint32_t ) xRegions[ lIndex ].pvBaseAddress ) | + ( ( uint32_t ) xRegions[ lIndex ].pvBaseAddress & ulBaseAddressMask ) | ( portMPU_REGION_VALID ) | ( ul - 1UL ); /* Region number. */ xMPUSettings->xRegion[ ul ].ulRegionAttribute = - ( prvGetMPURegionSizeSetting( xRegions[ lIndex ].ulLengthInBytes ) ) | + ( ulRegionSizeSetting ) | ( xRegions[ lIndex ].ulParameters ) | ( portMPU_REGION_ENABLE );