[tip: x86/urgent] x86/entry: Fix ptrace regression

From: tip-bot2 for Thomas Gleixner

Date: Tue Nov 03 2026 - 15:38:33 EST


The following commit has been merged into the x86/urgent branch of tip:

Commit-ID: b697faab2c92653ee13bd950c8de13551a78971d
Gitweb: https://git.kernel.org/tip/b697faab2c92653ee13bd950c8de13551a78971d
Author: Thomas Gleixner <tglx@xxxxxxxxxx>
AuthorDate: Sun, 01 Nov 2026 18:58:08 +01:00
Committer: Thomas Gleixner <tglx@xxxxxxxxxx>
CommitterDate: Tue, 03 Nov 2026 21:29:35 +01:00

x86/entry: Fix ptrace regression

The recent rework of the syscall entry code missed the historical detail of
the abuse of syscall number -1L to abort a syscall and keep modifications
by tracers to the return value, while a rewrite of the syscall number to an
invalid value undoes eventual tracer modification to the return value and
overwrite it with -ENOSYS.

Restore the original behaviour.

Fixes: 1b1f3b3e1b39 ("x86/entry: Get rid of the sys_ni_syscall() indirection")
Reported-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxx>
Tested-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
Link: https://patch.msgid.link/87ecd4xwlr.ffs@fw13
Closes: https://lore.kernel.org/all/20261022142425.3Wx-RrU5@xxxxxxxxxxxxx
---
arch/x86/entry/syscall_32.c | 4 ++++
arch/x86/entry/syscall_64.c | 26 ++++++++++++++++++++++----
2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/arch/x86/entry/syscall_32.c b/arch/x86/entry/syscall_32.c
index a59509b..ca97fa3 100644
--- a/arch/x86/entry/syscall_32.c
+++ b/arch/x86/entry/syscall_32.c
@@ -77,6 +77,10 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, unsigned
if (likely(nr < IA32_NR_syscalls)) {
nr = array_index_nospec(nr, IA32_NR_syscalls);
regs->ax = ia32_sys_call(regs, (unsigned int)nr);
+ } else {
+ /* See comment in do_syscall_64() */
+ if (nr != -1UL)
+ regs->ax = -ENOSYS;
}
}

diff --git a/arch/x86/entry/syscall_64.c b/arch/x86/entry/syscall_64.c
index 966d3d1..bb95ad0 100644
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -64,15 +64,20 @@ static __always_inline bool do_syscall_x64(struct pt_regs *regs, unsigned long n
return false;
}

-static __always_inline void do_syscall_x32(struct pt_regs *regs, unsigned long nr)
+static __always_inline bool do_syscall_x32(struct pt_regs *regs, unsigned long nr)
{
+ if (!IS_ENABLED(CONFIG_X86_X32_ABI))
+ return false;
+
/* Adjust the starting offset of the table */
nr -= __X32_SYSCALL_BIT;

- if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(nr < X32_NR_syscalls)) {
+ if (likely(nr < X32_NR_syscalls)) {
nr = array_index_nospec(nr, X32_NR_syscalls);
regs->ax = x32_sys_call(regs, (unsigned int)nr);
+ return true;
}
+ return false;
}

/* Returns true to return using SYSRET, or false to use IRET */
@@ -81,8 +86,21 @@ __visible noinstr bool do_syscall_64(struct pt_regs *regs, long nr)
if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
instrumentation_begin();

- if (!do_syscall_x64(regs, nr))
- do_syscall_x32(regs, nr);
+ if (unlikely(!do_syscall_x64(regs, nr))) {
+ /*
+ * The special treatment of syscall number -1 is a
+ * historical bogosity as the original code used -1 to
+ * indicate syscall abort, which is not distinguishable
+ * from a syscall which is invoked with syscall number
+ * -1. The historic behavior is to keep the return value
+ * in regs->ax unmodified in that case, which means it's
+ * either the preset value -ENOSYS or a value which was
+ * written by a tracer. If the syscall number is invalid
+ * but not -1 then overwrite any tracer modification.
+ */
+ if (!do_syscall_x32(regs, nr) && nr != -1L)
+ regs->ax = -ENOSYS;
+ }

instrumentation_end();
}