Skip to content

Mask MPU region base addresses when writing to MPU_RBAR to fix potential kernel region setting override - #1473

Open
CocoDico78 wants to merge 1 commit into
FreeRTOS:mainfrom
CocoDico78:main
Open

Mask MPU region base addresses when writing to MPU_RBAR to fix potential kernel region setting override#1473
CocoDico78 wants to merge 1 commit into
FreeRTOS:mainfrom
CocoDico78:main

Conversation

@CocoDico78

@CocoDico78 CocoDico78 commented Aug 20, 2026

Copy link
Copy Markdown

Description

While the docs are extensively mentioning that all regions should be aligned to power of two for correct MPU configuration, it is not explicit enough that not aligning (or not sanitizing) a user-defined region or a stack region may override MPU settings defined for higher priority kernel regions.

For example, for the ARM_CM3_MPU port, in port.c at lines 1318 (https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/ce221a8bb468e462ca6b435cef66a9636e00baf4/portable/GCC/ARM_CM3_MPU/port.c#L1318):

xMPUSettings->xRegion[ 0 ].ulRegionBaseAddress =
    ( ( uint32_t ) pxBottomOfStack ) |
    ( portMPU_REGION_VALID ) |
    ( portSTACK_REGION ); /* Region number. */

will escalate the actual region priority from 3 (value of portSTACK_REGION) to 7 (value of portPRIVILEGED_RAM_REGION) if pxBottomOfStack is maliciously or inadvertently shifted by 4 bytes (for example passing 0x20000004 instead of 0x20000000).

Same issue for user-defined regions which has the same bitwise calculation without masking.

This is in particular dangerous for implementers still using MPU wrappers v1 (which allows restricted tasks to create other restricted tasks, allowing to pass arbitrary misaligned stack pointers), and dangerous for implementers using MPU wrappers v2 who are not aware and might blindly trust user defined pointers thinking that the higher priority MPU regions will guard them.
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.

In this pull-request, we enforce such masking on the ARM_CM3_MPU port as a proof-of-concept, but once approved this PR can be extended to apply similar change to all other MPU ports.

Test Steps

  • Start a restricted task and pass a stack buffer starting at 0x20000004 in a user-defined region
  • Observe that the previously set portPRIVILEGED_RAM_REGION settings are no longer overriden by the user-defined region

Checklist:

  • I have tested my changes. No regression in existing tests.
  • I have modified and/or added unit-tests to cover the code changes in this Pull Request.

Related Issue

Follow up of a topic on the forum.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@aggarg

aggarg commented Aug 24, 2026

Copy link
Copy Markdown
Member

@CocoDico78 I would have expected the change to clear the VALID and REGION bits in the RBAR register. Would something like the following work instead?

#define RBAR_ADDRESS_MASK 0xFFFFFFE0
xMPUSettings->xRegion[ 0 ].ulRegionBaseAddress =
            ( ( ( uint32_t ) __SRAM_segment_start__ ) & RBAR_ADDRESS_MASK ) | /* Base address. */
            ( portMPU_REGION_VALID ) |
            ( portSTACK_REGION );  

@CocoDico78

CocoDico78 commented Aug 24, 2026

Copy link
Copy Markdown
Author

@aggarg

Would something like the following work instead?

#define RBAR_ADDRESS_MASK 0xFFFFFFE0

Yes, this static mask would absolutely protect the bits [0-4] and thus solve the main issue here. This static mask would however still not prevent users from passing improperly aligned addresses: say the the user is configuring a region of size 64; bit 5 could still be incorrectly set if the user passes a base address not aligned with 64 but aligned with 32.

You are suggesting a simpler, static mask, while I'm presenting a solution with a dynamic mask that guarantees that the written ADDR is always correctly aligned. What are your thoughts? I'm fine either way.

EDIT: the simplicity argument compels me. First, let's solve the VALID and REGION masking. The discussion on ensuring alignment in the ADDR field is independent.

Bits 0-4 are reserved for VALID and REGION, while ADDR  spans only bits 5-31. Masking ensures that a misaligned base address cannot modify less significant bits in the attribute reserved for other use. 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.

This change doesn't guarantee that only properly aligned addresses are written to the ADDR field of the register, but protects the VALID and REGION fields.
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants