arch: riscv: z_riscv_pmp_kernelmode_enable() does not clear trailing dynamic PMP entries
## 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 条评论