STM32: align MPU regions to actual size - #15441
Conversation
PMSAv7 requires MPU base addresses to be aligned to the actual configured region size. RASR.SIZE encodes region bytes as 2^(Size + 1), so using 1 << Size can still leave the base misaligned and let the MPU snap the protected window down. Use the actual region byte count when aligning and expanding dynamic MPU regions so DMA RAM remains fully covered by the non-cacheable mapping used for cache maintenance decisions. Addresses betaflight#14736.
|
Do you want to test this code? You can flash it directly from the Betaflight App:
WARNING: It may be unstable. Use only for testing! |
WalkthroughThe STM32 memory-protection configuration now uses power-of-two region bytes and iterative base realignment to ensure the MPU region covers the configured endpoint. PMSAv7 behavior and cache-maintenance considerations are documented in the updated comments. ChangesSTM32 MPU alignment
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Caution Review failedAn error occurred during the review process. Please try again later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
1 similar comment
|
Caution Review failedAn error occurred during the review process. Please try again later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Should fix Matek f743 as well as Geprc taker h743 (tested on the taker h743). |
|
@coderabbitai please review |
|
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/platform/STM32/memprot_hal.c (1)
83-93: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick winPrevent undefined behavior and infinite loops on large region sizes.
The calculation
1U << (msbpos + 1)invokes undefined behavior ifmsbpos >= 31, which can occur iflengthwraps around due to linker misconfiguration. Furthermore,alignedStart + regionBytescan overflow a 32-bit integer, resulting in a wrap-around to zero and causing an infinite loop.Use 64-bit arithmetic and cap
msbposto 31 to prevent these overflows and ensure the MPU's 5-bitSIZEfield remains valid.🔒️ Proposed fix to use 64-bit arithmetic and cap the region size
- uint32_t regionBytes = 1U << (msbpos + 1); - uint32_t alignedStart = start & ~(regionBytes - 1); + if (msbpos > 31) { + msbpos = 31; + } + + uint64_t regionBytes = 1ULL << (msbpos + 1); + uint64_t alignedStart = (uint64_t)start & ~(regionBytes - 1); while (alignedStart + regionBytes < region->end) { msbpos += 1; regionBytes <<= 1; - alignedStart = start & ~(regionBytes - 1); + alignedStart = (uint64_t)start & ~(regionBytes - 1); } - MPU_InitStruct.BaseAddress = alignedStart; + MPU_InitStruct.BaseAddress = (uint32_t)alignedStart; MPU_InitStruct.Size = msbpos;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/memprot_hal.c` around lines 83 - 93, Update the region-size calculation and growth loop around msbpos, regionBytes, and alignedStart to use uint64_t arithmetic, preventing shift and alignedStart + regionBytes overflow; cap msbpos at 31 before shifting or incrementing so the MPU_InitStruct.Size 5-bit field remains valid and the loop always terminates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/platform/STM32/memprot_hal.c`:
- Around line 83-93: Update the region-size calculation and growth loop around
msbpos, regionBytes, and alignedStart to use uint64_t arithmetic, preventing
shift and alignedStart + regionBytes overflow; cap msbpos at 31 before shifting
or incrementing so the MPU_InitStruct.Size 5-bit field remains valid and the
loop always terminates.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f38f2e35-0056-43c2-886e-045822480264
📒 Files selected for processing (1)
src/platform/STM32/memprot_hal.c
|
clamping a malformed MPU request to a gigantic region? You sure? Analyze the actual execution path in Betaflight, the possible values of length, msbpos, and regionBytes, and determine whether msbpos + 1 can realistically reach an undefined shift or overflow on supported hardware. If not, explain why. If so, propose the best fix. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== candidate files =='
fd -t f 'memprot_hal\.c|memprot.*\.h|.*mpu.*\.(c|h)$' src 2>/dev/null || true
printf '%s\n' '== outline =='
ast-grep outline src/platform/STM32/memprot_hal.c --items all 2>/dev/null || true
printf '%s\n' '== definitions and call sites =='
rg -n -C 5 --glob '*.{c,h}' \
'\bmemProtConfigure\s*\(|\bmsbpos\s*\(|\bregionBytes\b|\bMPU_REGION_SIZE\b|\bMPU_RASR_SIZE\b' \
src 2>/dev/null || true
printf '%s\n' '== relevant target configuration references =='
rg -n -C 3 --glob '*.{c,h}' \
'\bMEMPROT\b|\bMPU\b|DMA_RAM|dmaRam|blackbox' \
src/main/target src/platform 2>/dev/null || trueLength of output: 48016 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== generic implementation and region type =='
nl -ba src/platform/STM32/memprot_hal.c | sed -n '27,108p'
nl -ba src/main/drivers/memprot.h | sed -n '1,80p'
printf '%s\n' '== ARMv7 generic-path descriptor definitions =='
for f in src/platform/STM32/memprot_stm32g4xx.c src/platform/STM32/memprot_stm32h7xx.c; do
printf '\n-- %s --\n' "$f"
nl -ba "$f" | sed -n '1,160p'
done
printf '%s\n' '== linker-script candidates and DMA symbol definitions =='
fd -t f -e ld -e ld.in -e lds -e S . | while IFS= read -r f; do
if rg -n -C 4 '_dmaram_(start|end)__|dma_ram_(r|w|rw)_(start|end)|DMA_RAM' "$f" >/dev/null; then
printf '\n-- %s --\n' "$f"
rg -n -C 4 '_dmaram_(start|end)__|dma_ram_(r|w|rw)_(start|end)|DMA_RAM' "$f"
fi
done
printf '%s\n' '== MPU size encoding limits from vendored headers =='
rg -n -C 3 'MPU_REGION_SIZE_(32B|4GB)|MPU_RASR_SIZE_Msk|MPU_REGION_SIZE_Msk' \
lib src --glob '*.{h,c}' 2>/dev/null || trueLength of output: 1370 🏁 Script executed: #!/bin/bash
set -euo pipefail
show_range() {
file="$1"
start="$2"
end="$3"
printf '\n== %s (lines %s-%s) ==\n' "$file" "$start" "$end"
sed -n "${start},${end}{=;p;}" "$file"
}
show_range src/platform/STM32/memprot_hal.c 27 108
show_range src/main/drivers/memprot.h 1 80
show_range src/platform/STM32/memprot_stm32g4xx.c 1 160
show_range src/platform/STM32/memprot_stm32h7xx.c 1 160
printf '%s\n' '== linker-script DMA symbol definitions =='
fd -t f -e ld -e ld.in -e lds -e S . -x sh -c '
if rg -n -C 4 "_dmaram_(start|end)__|dma_ram_(r|w|rw)_(start|end)|DMA_RAM" "$1" >/dev/null; then
printf "\n-- %s --\n" "$1"
rg -n -C 4 "_dmaram_(start|end)__|dma_ram_(r|w|rw)_(start|end)|DMA_RAM" "$1"
fi
' sh {}
printf '%s\n' '== MPU encoding constants =='
rg -n -C 3 'MPU_REGION_SIZE_(32B|4GB)|MPU_RASR_SIZE_Msk|MPU_REGION_SIZE_Msk' \
lib src --glob '*.{h,c}' 2>/dev/null || trueLength of output: 71511 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== all MPU-region array definitions and generic implementation selection =='
rg -n -C 5 --glob '*.{c,h,mk,cmake,txt}' \
'\bmpuRegions\s*\[\s*\]|memprot_hal\.c|memprot_stm32(f7|h7|g4)' \
src make CMakeLists.txt 2>/dev/null || true
printf '%s\n' '== H743/G4 relevant linker memory declarations and DMA_RW_AXI placement =='
for f in src/platform/STM32/link/stm32_ram_h743.ld src/platform/STM32/link/stm32_flash_g4_split.ld; do
printf '\n-- %s --\n' "$f"
rg -n -C 8 '^(MEMORY|[[:space:]]*[A-Za-z0-9_]+[[:space:]]*\([^)]*\)[[:space:]]*:)|DMA_RW_AXI|DMA_RW_D2|\.DMA_RAM' "$f"
done
printf '%s\n' '== F7 system MPU setup and generic HAL references =='
rg -n -C 6 --glob '*.{c,h}' \
'\bHAL_MPU_ConfigRegion\s*\(|\bMPU_ConfigRegion\s*\(|\bmemProtConfigure\s*\(' \
src/platform/STM32 src/main 2>/dev/null || true
printf '%s\n' '== flsl implementation/contract =='
rg -n -C 5 --glob '*.{c,h}' '\bflsl\s*\(' src 2>/dev/null || trueLength of output: 21636
For this PR, the undefined-shift/overflow case is not reachable on supported Betaflight builds:
The sizing values are:
The first undefined shift would require So: no clamp and no fix needed for the real supported path in this PR. If we ever choose to harden this internal helper against a future broken linker descriptor, the right policy is to call 🐇🔍 |
PMSAv7 requires MPU base addresses to be aligned to the actual configured region size. RASR.SIZE encodes region bytes as 2^(Size + 1), so using 1 << Size can still leave the base misaligned and let the MPU snap the protected window down. Use the actual region byte count when aligning and expanding dynamic MPU regions so DMA RAM remains fully covered by the non-cacheable mapping used for cache maintenance decisions. Addresses betaflight#14736. Co-authored-by: morrob955 <196944784+morrob955@users.noreply.github.com> Co-authored-by: blckmn <blckmn@users.noreply.github.com>
Summary
2^(Size + 1)starttoendrangeValidation
make TARGET=STM32H743 V=0Addresses #14736.
Summary by CodeRabbit