Skip to content

Commit 14fd25c

Browse files
gmarullclaude
andcommitted
kernel: keep empty MPU slots from wiping the previous region
arch_thread_regions_set() encodes an unused per-thread region slot as RBAR=0 / RASR=0. On ARMv7-M the context switch streams the four slots through MPU_RBAR..MPU_RASR_A3, and an RBAR write with VALID clear does not change RNR: it updates the region RNR already points at, i.e. the one the previous slot just programmed. For App and Worker the empty slot 3 therefore lands on region 6 and its RASR=0 disables the task stack guard. The FreeRTOS port ORed VALID|region into every slot, which is why v4.36 firmware still faults on the guard while v4.37+ lets an app stack overflow walk into the kernel heap unnoticed. Keep VALID|region on empty slots (RASR stays 0, so the region is simply disabled). ARMv8-M selects the region through RNR and the aliases and is unaffected. Observed on qemu_flint: with the App thread running, region 6 read back as RBAR=0x6 RASR=0 while the thread's saved MPU words held the guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
1 parent 185551c commit 14fd25c

1 file changed

Lines changed: 7 additions & 3 deletions

File tree

kernel/arch/arm/arch.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,21 +139,25 @@ void arch_thread_init(struct pbl_thread *t, void (*entry)(void *), void *arg) {
139139

140140
// The port expects the attribute word to hold RASR/RLAR verbatim and derives
141141
// RBAR from the base: on ARMv7-M the region number and VALID bit are ORed in
142-
// so no RNR write is needed per region.
142+
// so no RNR write is needed per region. An empty slot must still carry them:
143+
// an RBAR write with VALID clear lands on whatever region RNR last selected,
144+
// i.e. it would wipe the slot programmed just before it.
143145
void arch_thread_regions_set(struct pbl_thread *t, const MpuRegion *const *regions) {
144146
for (unsigned int i = 0; i < NUM_MPU_REGIONS; i++) {
145147
const MpuRegion *r = regions ? regions[i] : NULL;
146148
uint32_t rbar = 0;
147149
uint32_t attr = 0;
150+
#ifndef CONFIG_MPU_TYPE_ARMV8M
151+
rbar = MPU_RBAR_VALID_Msk | (FIRST_MPU_REGION + i);
152+
#endif
148153
if (r != NULL) {
149154
KERNEL_ASSERT(r->region_num == FIRST_MPU_REGION + i);
150155
uint32_t base_reg;
151156
mpu_get_register_settings(r, &base_reg, &attr);
152157
#ifdef CONFIG_MPU_TYPE_ARMV8M
153158
rbar = base_reg;
154159
#else
155-
rbar = (base_reg & ~(MPU_RBAR_VALID_Msk | MPU_RBAR_REGION_Msk)) | MPU_RBAR_VALID_Msk |
156-
(FIRST_MPU_REGION + i);
160+
rbar |= base_reg & ~(MPU_RBAR_VALID_Msk | MPU_RBAR_REGION_Msk);
157161
#endif
158162
}
159163
t->backend.arch.mpu[2 * i] = rbar;

0 commit comments

Comments
 (0)