arch/risc-v: fix fault handler misattributing kernel faults to user tasks

riscv_fault_handler() only checked the task type and SYSCALL flag, so a
fault taken inside an interrupt handler (running in kernel mode) was
blamed on the user task that happened to be interrupted and killed with
SIGSEGV, hiding the real kernel bug.  Use the STATUS_PPP bit of the trap
frame to tell whether the fault originated from user mode: only then is
it safe to kill the task.

Signed-off-by: liang.huang <liang.huang@houmo.ai>
This commit is contained in:
liang.huang 2026-07-18 11:33:30 +08:00 • committed by Xiang Xiao
parent 41837f0699
commit 32f578f37a

View file

@ -87,10 +87,11 @@ static const char *g_reasons_str[RISCV_MAX_EXCEPTION + 1] =
*
* Description:
* Handle a fault caused by the running task. If the task is a user task
* not currently in a syscall, kill it with SIGSEGV instead of
* taking down the whole system. Otherwise (kernel thread, or a fault
* while already in kernel context on behalf of a syscall) there is no
* safe task to kill, so panic.
* not currently in a syscall or interrupt context, kill it with SIGSEGV
* instead of taking down the whole system. Otherwise (kernel thread, a
* fault while already in kernel context on behalf of a syscall, or a
* fault while handling an interrupt) there is no safe task to kill, so
* panic.
*
* Input Parameters:
* cause - The (masked) machine cause of the exception, used for the
@ -104,8 +105,14 @@ static void riscv_fault_handler(uintreg_t cause, void *regs)
#ifdef CONFIG_ARCH_KERNEL_STACK
struct tcb_s *tcb = this_task();
/* The STATUS_PPP check alone is enough: any kernel-mode fault (kernel
* thread, syscall body, or interrupt) has STATUS_PPP != 0. The other
* two checks are kept as defensive redundancy.
*/
if (((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL) &&
((tcb->flags & TCB_FLAG_SYSCALL) == false))
((tcb->flags & TCB_FLAG_SYSCALL) == false) &&
!(((uintreg_t *)regs)[REG_INT_CTX] & STATUS_PPP))
{
struct tcb_s *ptcb = nxsched_get_tcb(tcb->group->tg_pid);