Re: [PATCH] sh: prevent ptrace from changing privileged SR bits
From: Rich Felker
Date: Fri Aug 21 2026 - 08:31:31 EST
On Fri, Aug 21, 2026 at 10:33:36AM +0000, Jérémy Jean wrote:
> PTRACE_POKEUSR writes every word in struct pt_regs, including SR, without
> filtering privileged bits. The general register regset setter exposes the
> same write through PTRACE_SETREGS and PTRACE_SETREGSET.
>
> The exception return path restores the saved SR with rte after adjusting
> only the interrupt mask. An unprivileged same-UID tracer can set SR.MD and
> make its tracee resume at a user address in privileged mode.
>
> Preserve the saved non-user SR bits in both write paths and accept only
> the bits in SR_USER_MASK. Recompose SR after user_regset_copyin() even on
> error because the copy may have updated a prefix of the register set.
>
> Assisted-by: Codex:gpt-5
I'm only nominally a maintainer for the arch anymore, but about this:
could we please not?
> Signed-off-by: Jérémy Jean <Jeremy.Jean@xxxxxxxxxxxxxxxxx>
> ---
> arch/sh/kernel/ptrace_32.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c
> index 8794081483fb..bb69c35cb9a7 100644
> --- a/arch/sh/kernel/ptrace_32.c
> +++ b/arch/sh/kernel/ptrace_32.c
> @@ -146,6 +146,7 @@ static int genregs_set(struct task_struct *target,
> const void *kbuf, const void __user *ubuf)
> {
> struct pt_regs *regs = task_pt_regs(target);
> + unsigned long sr = regs->sr & ~SR_USER_MASK;
> int ret;
>
> ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> @@ -159,6 +160,7 @@ static int genregs_set(struct task_struct *target,
> if (!ret)
> user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
> sizeof(struct pt_regs), -1);
> + regs->sr = (regs->sr & SR_USER_MASK) | sr;
>
> return ret;
> }
> @@ -391,6 +393,11 @@ long arch_ptrace(struct task_struct *child, long request,
> addr > sizeof(struct user) - 3)
> break;
>
> + if (addr == offsetof(struct pt_regs, sr)) {
> + unsigned long sr = get_stack_long(child, addr);
> +
> + data = (data & SR_USER_MASK) | (sr & ~SR_USER_MASK);
> + }
> if (addr < sizeof(struct pt_regs))
> ret = put_stack_long(child, addr, data);
> else if (addr >= offsetof(struct user, fpu) &&
> --
> 2.47.3
While I'm not against having ptrace enforce this too, I think for
safety the return path from kernelspace to userspace should also
enforce it. Blindly restoring privileged mode bits from the task
context structure when returning to userspace is just *asking for*
this exact same vuln to arise again and again via other vectors.
It should be possible to enforce this in the same place the interrupt
mask is adjusted on return to user.
Rich