[PATCH 2/2] lib: sbi: sse: fix SSE handler sstatus leakage
Zhanpeng Zhang
zhangzhanpeng.jasper at bytedance.com
Thu Apr 30 03:23:13 PDT 2026
SSE can interrupt kernel execution at any point and run a complex
handler. PMU-SSE is a real-world example: its handler runs the
perf overflow path, and `perf top` repeatedly enters the SSE handler and
completion paths when PMU-SSE is enabled. Any synthetic handler status
that leaks into the SSE-interrupted context is therefore quickly exposed.
SSE injection rewrites SIE, SPIE and SPP to enter the registered S-mode
handler. The old completion path reconstructed SIE from the handler trap
state and restored SPIE/SPP from the interrupted attributes instead of
restoring the S-mode status bits that were interrupted by the event.
That makes SIE/SPIE/SPP handler-contaminated resume state. The resulting
SSE handler sstatus leakage can make PMU-SSE perf top resume with
mismatched privilege and interrupt state, causing an "environment call
from U-mode" panic. This was reproducible on the previous version with
stress tests like `perf top` or `perf record -a`.
Save the interrupted SIE/SPIE/SPP bits in private SSE resume state before
constructing the handler context and restore them when the handler
completes. Keep using the interrupted SPP attribute to select the final
mret privilege level.
Fixes: c8cdf01d8f3a ("lib: sbi: Add support for Supervisor Software Events extension")
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper at bytedance.com>
---
lib/sbi/sbi_sse.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/lib/sbi/sbi_sse.c b/lib/sbi/sbi_sse.c
index 0544449d..1c1a0e80 100644
--- a/lib/sbi/sbi_sse.c
+++ b/lib/sbi/sbi_sse.c
@@ -77,12 +77,17 @@ struct sse_ipi_inject_data {
* M-mode-owned state.
*/
struct sse_resume_state {
+ /* SIE/SPIE/SPP bits from mstatus, used to restore S-mode state. */
+ unsigned long sstatus_bits;
/* Complete hstatus value, used to restore H-mode virtualization state. */
unsigned long hstatus;
/* MPV bit from mstatus/mstatusH, used to restore virtualization state. */
bool prev_virt;
};
+#define SSE_RESUME_SSTATUS_MASK \
+ (SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP)
+
struct sbi_sse_event_attrs {
unsigned long status;
unsigned long prio;
@@ -568,6 +573,7 @@ static void sse_event_inject(struct sbi_sse_event *e,
i_ctx->a6 = regs->a6;
i_ctx->a7 = regs->a7;
i_ctx->sepc = csr_read(CSR_SEPC);
+ r_ctx->sstatus_bits = regs->mstatus & SSE_RESUME_SSTATUS_MASK;
r_ctx->prev_virt = sbi_regs_from_virt(regs);
@@ -631,7 +637,7 @@ static void sse_event_resume(struct sbi_sse_event *e,
regs->mepc = csr_read(CSR_SEPC);
regs->mstatus &= ~MSTATUS_MPP;
- if (regs->mstatus & MSTATUS_SPP)
+ if (i_ctx->flags & SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPP)
regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
if (misa_extension('H')) {
@@ -649,17 +655,8 @@ static void sse_event_resume(struct sbi_sse_event *e,
csr_write(CSR_HSTATUS, r_ctx->hstatus);
}
- regs->mstatus &= ~MSTATUS_SIE;
- if (regs->mstatus & MSTATUS_SPIE)
- regs->mstatus |= MSTATUS_SIE;
-
- regs->mstatus &= ~MSTATUS_SPIE;
- if (i_ctx->flags & SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPIE)
- regs->mstatus |= MSTATUS_SPIE;
-
- regs->mstatus &= ~MSTATUS_SPP;
- if (i_ctx->flags & SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPP)
- regs->mstatus |= MSTATUS_SPP;
+ regs->mstatus &= ~SSE_RESUME_SSTATUS_MASK;
+ regs->mstatus |= r_ctx->sstatus_bits;
regs->mstatus &= ~MSTATUS_SPELP;
if (i_ctx->flags & SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPELP)
--
2.50.1 (Apple Git-155)
More information about the opensbi
mailing list