ITADN

arch: riscv: z_riscv_pmp_kernelmode_enable() does not clear trailing dynamic PMP entries

#113868Closedhongquan-prog 创建于 2026-07-18
H
hongquan-progcommented
## Describe the bug `z_riscv_pmp_kernelmode_enable()` is called on every context switch to install the current thread's M-mode PMP entries. It writes only the range `[global_end_index, thread->arch.m_mode_pmp_end_index)` and passes `clear_trailing_entries = false`: https://github.com/zephyrproject-rtos/zephyr/blob/da9b7b9d257b71098ed2c0d156d137420ea86779/arch/riscv/core/pmp.c#L952-L956 As a result, dynamic PMP slots beyond `m_mode_pmp_end_index` are left untouched in the hardware PMP registers. Those slots may still contain entries from the previously running thread. The helper `write_pmp_entries()` supports clearing trailing entries when the flag is `true`: https://github.com/zephyrproject-rtos/zephyr/blob/da9b7b9d257b71098ed2c0d156d137420ea86779/arch/riscv/core/pmp.c#L404-L417 and the corresponding assembly routine `z_riscv_write_pmp_entries()` zeroes out the remaining slots based on that flag. For comparison, the U-mode equivalent `z_riscv_pmp_usermode_enable()` already passes `true`: https://github.com/zephyrproject-rtos/zephyr/blob/da9b7b9d257b71098ed2c0d156d137420ea86779/arch/riscv/core/pmp.c#L1155-L1160 This inconsistency means the M-mode path can leak PMP state across context switches. The `false /* no need to clear to the end */` assumption originally relied on every thread using the same number of M-mode dynamic PMP slots. That was true when the implementation always added a fixed stack-guard + catch-all pair. It is no longer guaranteed: `set_pmp_entry()` may select different address-matching modes (e.g., one NAPOT slot versus two TOR slots) for stack guards depending on alignment, and optional per-thread features can change the number of entries. Once two threads have different `m_mode_pmp_end_index` values, stale trailing entries from the previous thread become observable. ## Regression - [ ] This is a regression. ## Steps to reproduce The problem can be observed by switching between two threads whose `m_mode_pmp_end_index` values differ and then reading the hardware PMP CSRs after the switch: 1. Create thread A with a larger `m_mode_pmp_end_index` (for example, by forcing its stack guard to consume an extra TOR slot). 2. Run thread A on a CPU, then switch to thread B whose `m_mode_pmp_end_index` is smaller. 3. Read `pmpaddrN` / `pmpcfgN` for slots `B_end .. A_end - 1`. Expected: those slots are cleared. Actual: they still contain thread A's PMP entries. In typical configurations all threads end up with the same number of M-mode PMP entries, so the bug is latent. It becomes visible as soon as a thread uses more dynamic slots than the next thread to run on the same CPU. ## Expected behavior `z_riscv_pmp_kernelmode_enable()` should clear trailing dynamic PMP entries after writing the current thread's entries, matching `z_riscv_pmp_usermode_enable()`. The third argument to `write_pmp_entries()` should be `true` instead of `false`. `index_limit` is already bounded by `PMP_USABLE_SLOTS`, so this will not affect locked global entries beyond that limit. ## Impact What is the impact of this bug? - Intermittent – Occurs occasionally; hard to reproduce. When two threads have different M-mode PMP usage, the trailing entries from the previous thread remain active. This can: - Leak access permissions: a stale catch-all or permission-granting entry from the previous thread may remain effective for the current thread. - Cause spurious faults: a stale `PMP_NONE` stack-guard entry from the previous thread may cover an address that the current thread legitimately needs to access. - Make isolation dependent on CPU scheduling history, because PMP CSRs are per-CPU. ## Relevant log output No specific log output available. ## Environment - OS: Linux - Toolchain: Zephyr SDK 1.0.1 (riscv64-zephyr-elf-gcc 14.3.0) - Board/target: `qemu_riscv64/qemu_virt_riscv64/smp` and other RISC-V SMP targets using `CONFIG_PMP_KERNEL_MODE_DYNAMIC` ## Additional context - Related files: `arch/riscv/core/pmp.c`, `arch/riscv/core/pmp.S` - Related Kconfig: `CONFIG_PMP_KERNEL_MODE_DYNAMIC`, `CONFIG_PMP_STACK_GUARD`
关闭于 2026-07-19 1 条评论