From: Kees Cook <kees@kernel.org>
To: Jinjie Ruan <ruanjinjie@huawei.com>
Cc: Will Deacon <will@kernel.org>,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Mark Rutland <mark.rutland@arm.com>,
Yiqi Sun <sunyiqixm@gmail.com>,
Catalin Marinas <catalin.marinas@arm.com>
Subject: Re: [PATCH] arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates
Date: Wed, 15 Jul 2026 20:05:34 -0700 [thread overview]
Message-ID: <202607152004.DEA95D63@keescook> (raw)
In-Reply-To: <ac39d21e-8800-4c4e-885e-4baf7af2a106@huawei.com>
On Thu, Jul 16, 2026 at 10:57:34AM +0800, Jinjie Ruan wrote:
>
>
> On 7/14/2026 10:35 PM, Will Deacon wrote:
> > When seccomp support was originally added to arm64 in a1ae65b21941
> > ("arm64: add seccomp support"), seccomp was erroneously called _before_
> > the ptrace syscall-enter-stop and therefore the tracer could trivially
> > manipulate the syscall register state after the seccomp check had
> > passed. This was subsequently fixed in a5cd110cb836 ("arm64/ptrace: run
> > seccomp after ptrace") by moving the seccomp check after the tracer has
> > run. Unfortunately, a decade later, that fix has been reported to be
> > incomplete.
> >
> > On arm64, both the first argument to a syscall and its eventual return
> > value are allocated to register x0. In order to facilitate syscall
> > restarting and querying of syscall arguments on the syscall exit path,
> > the original value of x0 is stashed in 'struct pt_regs::orig_x0' early
> > during the syscall entry path and is returned for the first argument by
> > syscall_get_arguments(). Unlike 32-bit Arm, this stashed value is not
> > directly exposed via ptrace() and so changes to register x0 made by the
> > tracer on a syscall-enter-stop are not reflected in 'orig_x0'. This
> > means that seccomp and audit can observe a stale value for the register
> > compared to the argument that will be observed by the actual syscall.
> >
> > Re-sync 'orig_x0' from x0 on the syscall entry path following a
> > potential ptrace stop (i.e. PTRACE_EVENTMSG_SYSCALL_ENTRY or
> > SECCOMP_RET_TRACE). This behaviour is limited to native tasks (because
> > compat tasks expose 'orig_r0' to ptrace) where the syscall is not being
> > skipped (because x0 is updated to hold the return value of -ENOSYS in
> > that case).
> >
> > Cc: Kees Cook <kees@kernel.org>
> > Cc: Jinjie Ruan <ruanjinjie@huawei.com>
> > Cc: Mark Rutland <mark.rutland@arm.com>
> > Reported-by: Yiqi Sun <sunyiqixm@gmail.com>
> > Link: https://lore.kernel.org/all/20260529065444.1336608-1-sunyiqixm@gmail.com/
> > Suggested-by: Catalin Marinas <catalin.marinas@arm.com>
> > Fixes: a5cd110cb836 ("arm64/ptrace: run seccomp after ptrace")
> > Signed-off-by: Will Deacon <will@kernel.org>
> > ---
> > arch/arm64/kernel/ptrace.c | 24 ++++++++++++++++++++++++
> > 1 file changed, 24 insertions(+)
> >
> > diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> > index 4d08598e2891..57e8c6714d44 100644
> > --- a/arch/arm64/kernel/ptrace.c
> > +++ b/arch/arm64/kernel/ptrace.c
> > @@ -2408,6 +2408,21 @@ static void report_syscall_exit(struct pt_regs *regs)
> > }
> > }
> >
> > +static void update_syscall_orig_x0_after_ptrace(struct pt_regs *regs)
> > +{
> > + /*
> > + * Keep orig_x0 authoritative so that seccomp (via
> > + * syscall_get_arguments()), audit and the restart path all see the same
> > + * first argument the syscall is dispatched with, even if it has been
> > + * updated by a tracer. Skip this for NO_SYSCALL (set either by the user
> > + * or the tracer), as regs[0] holds the return value (see the comment in
> > + * el0_svc_common()) and can be unwound using syscall_rollback().
> > + * For compat tasks, orig_r0 is provided directly through GPR index 17.
> > + */
> > + if (!is_compat_task() && regs->syscallno != NO_SYSCALL)
> > + regs->orig_x0 = regs->regs[0];
> > +}
> > +
> > int syscall_trace_enter(struct pt_regs *regs)
> > {
> > unsigned long flags = read_thread_flags();
> > @@ -2417,12 +2432,21 @@ int syscall_trace_enter(struct pt_regs *regs)
> > ret = report_syscall_entry(regs);
> > if (ret || (flags & _TIF_SYSCALL_EMU))
> > return NO_SYSCALL;
> > +
> > + /*
> > + * Ensure ptrace changes to x0 are visible to seccomp
> > + * ptrace exits (SECCOMP_RET_TRACE).
> > + */
> > + update_syscall_orig_x0_after_ptrace(regs);
> > }
> >
> > /* Do the secure computing after ptrace; failures should be fast. */
> > if (secure_computing() == -1)
> > return NO_SYSCALL;
> >
> > + /* Ensure seccomp updates to x0 are visible to audit. */
> > + update_syscall_orig_x0_after_ptrace(regs);
>
>
> Hi, will
>
> I think unconditionally updating orig_x0 here is unnecessary, we could
> Expand seccomp check in place as below the same as generic entry.
>
> In this way, in most cases where seccomp is not used, the overhead of
> updating orig_x0 is eliminated. Moreover, we only need to define an
> architecture-specific version of the seccomp function, thus avoiding the
> pain of switching from arm64 to the generic entry.
>
> So this patch can be like below.
>
> int syscall_trace_enter(struct pt_regs *regs)
> {
> unsigned long flags = read_thread_flags();
> @@ -2420,12 +2435,24 @@ int syscall_trace_enter(struct pt_regs *regs)
>
> /* ptrace might have changed work flags */
> flags = read_thread_flags();
> + /*
> + * Ensure ptrace changes to x0 during a regular
> syscall-enter-stop
> + * (PTRACE_SYSCALL) are visible to subsequent seccomp trace
> + * and audit checking.
> + */
> + update_syscall_orig_x0_after_ptrace(regs);
> }
>
> /* Do the secure computing after ptrace; failures should be fast. */
> if (unlikely(flags & _TIF_SECCOMP)) {
> if (!__seccomp_permit_syscall())
> return NO_SYSCALL;
> +
> + /*
> + * Ensure tracer changes to x0 during seccomp ptrace
> exit processing
> + * (SECCOMP_RET_TRACE) are visible to audit.
> + */
> + update_syscall_orig_x0_after_ptrace(regs);
> }
>
>
> Author: Jinjie Ruan <ruanjinjie@huawei.com>
> Date: Tue Oct 29 19:08:03 2024 +0800
>
> arm64: ptrace: Expand seccomp check in place
>
> Refactor syscall_trace_enter() by open-coding the seccomp check
> to align with the generic entry framework. While the original call to
> seccomp_permit_syscall() internally re-reads the thread flags and is
> therefore safe against flag changes during ptrace stops, the new
> open-coded version must explicitly re-read the flags after ptrace
> handling to preserve that safety.
>
> [Background]
> The generic entry implementation expands the seccomp check in-place
> instead of using the seccomp_permit_syscall() wrapper. It directly
> tests SYSCALL_WORK_SECCOMP and calls the underlying
> __seccomp_permit_syscall() function to handle syscall filtering.
>
> [Changes]
> 1. After ptrace handling, re-read thread flags:
> This ensures that any _TIF_SECCOMP set during the ptrace stop is
> observed before the seccomp check.
>
> 2. Open-code seccomp check:
> - Instead of calling the seccomp_permit_syscall() wrapper, explicitly
> check the updated 'flags' parameter for _TIF_SECCOMP.
> - Call __seccomp_permit_syscall() directly if the flag is set.
>
> [Why this matters]
> - Aligns the arm64 syscall path with the generic entry implementation,
> simplifying future migration to the generic entry framework.
>
> - No functional changes are intended; seccomp behavior remains
> identical.
> The explicit re-read ensures the open-coded version retains the same
> safety as the original wrapper, preventing the race condition
> described
> in the generic entry fix.
>
> - Performance: Non-ptrace fast path avoids atomic test_bit overhead via
> cached flags.
>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Link:
> https://lore.kernel.org/all/20260713025712.416366-1-ruanjinjie@huawei.com/
> Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
> Reviewed-by: Kevin Brodsky <kevin.brodsky@arm.com>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>
> diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> index 5709e9d3c321..941752656ea6 100644
> --- a/arch/arm64/kernel/ptrace.c
> +++ b/arch/arm64/kernel/ptrace.c
> @@ -2417,11 +2417,16 @@ int syscall_trace_enter(struct pt_regs *regs)
> ret = report_syscall_entry(regs);
> if (ret || (flags & _TIF_SYSCALL_EMU))
> return NO_SYSCALL;
> +
> + /* ptrace might have changed the flags */
> + flags = read_thread_flags();
> }
>
> /* Do the secure computing after ptrace; failures should be fast. */
> - if (!seccomp_permit_syscall())
> - return NO_SYSCALL;
> + if (unlikely(flags & _TIF_SECCOMP)) {
> + if (!__seccomp_permit_syscall())
> + return NO_SYSCALL;
> + }
>
> if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
> trace_sys_enter(regs, regs->syscallno);
Do we have a corresponding seccomp_bpf.c selftest we can add for this? I
would really like to have a regression test that would catch this
issue...
-Kees
--
Kees Cook
next prev parent reply other threads:[~2026-07-16 3:05 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-14 14:35 [PATCH] arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates Will Deacon
2026-07-15 11:39 ` Jinjie Ruan
2026-07-15 13:16 ` Will Deacon
2026-07-16 2:09 ` Jinjie Ruan
2026-07-16 2:57 ` Jinjie Ruan
2026-07-16 3:05 ` Kees Cook [this message]
2026-07-16 3:25 ` Jinjie Ruan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=202607152004.DEA95D63@keescook \
--to=kees@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=ruanjinjie@huawei.com \
--cc=sunyiqixm@gmail.com \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.