LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [patch 18/18] entry, treewide: Make syscall_enter_from_user_mode[_work]() indicate syscall execution
From: Shrikanth Hegde @ 2026-07-08  5:21 UTC (permalink / raw)
  To: Thomas Gleixner, LKML
  Cc: Peter Zijlstra, Michal Suchánek, Jonathan Corbet,
	Arnd Bergmann, Mark Rutland, Huacai Chen, Michael Ellerman,
	Paul Walmsley, Palmer Dabbelt, Sven Schnelle, linux-doc,
	loongarch, linuxppc-dev, linux-riscv, linux-s390, Kees Cook, x86,
	Jinjie Ruan, Andy Lutomirski, Oleg Nesterov, Richard Henderson,
	Russell King, Catalin Marinas, Guo Ren, Geert Uytterhoeven,
	Thomas Bogendoerfer, Helge Deller, Yoshinori Sato,
	Richard Weinberger, Chris Zankel, linux-arm-kernel, linux-alpha,
	linux-csky, linux-m68k, linux-mips, linux-parisc, linux-sh,
	linux-um, Vineet Gupta, Will Deacon, Brian Cain, Michal Simek,
	Dinh Nguyen, David S. Miller, Andreas Larsson, linux-snps-arc,
	linux-hexagon, linux-openrisc, sparclinux, linux-arch
In-Reply-To: <20260707190254.603111179@kernel.org>

Hi Thomas.

On 7/8/26 12:37 AM, Thomas Gleixner wrote:
> From: Michal Suchánek <msuchanek@suse.de>
> 
> The return values of syscall_enter_from_user_mode[_work]() are
> non-intuitive. Both functions return the syscall number which should be
> invoked by the architecture specific syscall entry code. The returned
> number can be:
> 
>    - the unmodified syscall number which was handed in by the caller
> 
>    - a modified syscall number (ptrace, seccomp, trace/probe/bpf)
> 
> That has an additional twist. If the return value is -1L then the caller is
> not allowed to modify the return value as that indicates that the modifying
> entity requests to abort the syscall and set the return value already. That
> can obviously not be differentiated from a syscall which handed in -1 as
> syscall number.
> 
> The established way to deal with that is:
> 
>      set_return_value(regs, -ENOSYS);
>      nr = syscall_enter_from_user_mode(regs, nr);
>      if ((unsigned)nr < SYSCALLNR_MAX)
>      	handle_syscall(regs, nr);
>      else if (nr != -1)
>      	set_return_value(regs, -ENOSYS);
> 
> The latter is obviously redundant, but that's just a leftover of the
> historical evolution of this code. S390 has some special requirements here,
> which can be avoided when the return value is not ambiguous.
> 
> Now that the functions which modify the syscall number and want to abort
> are converted to indicate that with a boolean return value, it's obvious to
> hand this through to the callers.
> 
> Rework syscall_enter_from_user_mode[_work]) so they take a pointer to the
> syscall number and return a boolean, which indicates whether the syscall
> should be handled or not.
> 
> That's not only more intuitive, it also results in slightly denser
> executable code on x86 at least, but perf results are neutral and within
> the noise.
> 
> [ tglx: Adopted it to the changes in the generic entry code, fixed up the
>    	32-bit fallout and rewrote change log ]
> 
> Signed-off-by: Michal Suchánek <msuchanek@suse.de>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: Jonathan Corbet <corbet@lwn.net>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Huacai Chen <chenhuacai@kernel.org>
> Cc: Michael Ellerman <mpe@ellerman.id.au>
> Cc: Shrikanth Hegde <sshegde@linux.ibm.com>
> Cc: Paul Walmsley <pjw@kernel.org>
> Cc: Palmer Dabbelt <palmer@dabbelt.com>
> Cc: Sven Schnelle <svens@linux.ibm.com>
> Cc: linux-doc@vger.kernel.org
> Cc: loongarch@lists.linux.dev
> Cc: linuxppc-dev@lists.ozlabs.org
> Cc: linux-riscv@lists.infradead.org
> Cc: linux-s390@vger.kernel.org
> ---
>   Documentation/core-api/entry.rst |   18 +++++++++++-------
>   arch/loongarch/kernel/syscall.c  |   14 +++++++-------
>   arch/powerpc/kernel/syscall.c    |    3 ++-
>   arch/riscv/kernel/traps.c        |   11 +++++------
>   arch/s390/kernel/syscall.c       |    7 +++++--
>   arch/x86/entry/syscall_32.c      |   25 ++++++++++++-------------
>   arch/x86/entry/syscall_64.c      |   12 ++++++------
>   include/linux/entry-common.h     |   12 +++++-------
>   8 files changed, 53 insertions(+), 49 deletions(-)
> 
> --- a/Documentation/core-api/entry.rst
> +++ b/Documentation/core-api/entry.rst
> @@ -68,16 +68,20 @@ low-level C code must not be instrumente
>     noinstr void syscall(struct pt_regs *regs, int nr)
>     {
>   	arch_syscall_enter(regs);
> -	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
> -
> -	instrumentation_begin();
> -	if (!invoke_syscall(regs, nr) && nr != -1)
> -	 	result_reg(regs) = __sys_ni_syscall(regs);
> -	instrumentation_end();
> -
> +	result_reg(regs) = -ENOSYS;
> +	if (syscall_enter_from_user_mode_randomize_stack(regs, &nr)) {
> +		instrumentation_begin();
> +		if (!invoke_syscall(regs, nr))
> +			result_reg(regs) = __sys_ni_syscall(regs);
> +		instrumentation_end();
> +	}
>   	syscall_exit_to_user_mode(regs);
>     }
>   
> +It is required that either the low level assembly code or the syscall
> +function sets the result register to -ENOSYS before invoking the generic
> +code.
> +
>   syscall_enter_from_user_mode_randomize_stack() first invokes
>   enter_from_user_mode_randomize_stack() which establishes state in the
>   following order:
> --- a/arch/loongarch/kernel/syscall.c
> +++ b/arch/loongarch/kernel/syscall.c
> @@ -57,8 +57,8 @@ typedef long (*sys_call_fn)(unsigned lon
>   
>   void noinstr __no_stack_protector do_syscall(struct pt_regs *regs)
>   {
> -	unsigned long nr;
>   	sys_call_fn syscall_fn;
> +	unsigned long nr;
>   
>   	nr = regs->regs[11];
>   	/* Set for syscall restarting */
> @@ -69,12 +69,12 @@ void noinstr __no_stack_protector do_sys
>   	regs->orig_a0 = regs->regs[4];
>   	regs->regs[4] = -ENOSYS;
>   
> -	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
> -
> -	if (nr < NR_syscalls) {
> -		syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)];
> -		regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6],
> -					   regs->regs[7], regs->regs[8], regs->regs[9]);
> +	if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
> +		if (nr < NR_syscalls) {
> +			syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)];
> +			regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6],
> +						   regs->regs[7], regs->regs[8], regs->regs[9]);
> +		}
>   	}
>   
>   	syscall_exit_to_user_mode(regs);
> --- a/arch/powerpc/kernel/syscall.c
> +++ b/arch/powerpc/kernel/syscall.c
> @@ -18,7 +18,8 @@ notrace long system_call_exception(struc
>   	long ret;
>   	syscall_fn f;
>   
> -	r0 = syscall_enter_from_user_mode_randomize_stack(regs, r0);
> +	if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0))
> +		return syscall_get_error(current, regs);
>   

There is one missing )

diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
index d85894bdb6a2..1440dcabe052 100644
--- a/arch/powerpc/kernel/syscall.c
+++ b/arch/powerpc/kernel/syscall.c
@@ -18,7 +18,7 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
         long ret;
         syscall_fn f;
  
-       if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0))
+       if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0)))
                 return syscall_get_error(current, regs);
  
         if (unlikely(r0 >= NR_syscalls)) {


>   	if (unlikely(r0 >= NR_syscalls)) {
>   		if (unlikely(trap_is_unsupported_scv(regs))) {
> --- a/arch/riscv/kernel/traps.c
> +++ b/arch/riscv/kernel/traps.c
> @@ -332,13 +332,12 @@ void do_trap_ecall_u(struct pt_regs *reg
>   
>   		riscv_v_vstate_discard(regs);
>   
> -		syscall = syscall_enter_from_user_mode_randomize_stack(regs, syscall);
> -
> -		if (syscall >= 0 && syscall < NR_syscalls) {
> -			syscall = array_index_nospec(syscall, NR_syscalls);
> -			syscall_handler(regs, syscall);
> +		if (syscall_enter_from_user_mode_randomize_stack(regs, &syscall)) {
> +			if (syscall >= 0 && syscall < NR_syscalls) {
> +				syscall = array_index_nospec(syscall, NR_syscalls);
> +				syscall_handler(regs, syscall);
> +			}
>   		}
> -
>   		syscall_exit_to_user_mode(regs);
>   	} else {
>   		irqentry_state_t state = irqentry_nmi_enter(regs);
> --- a/arch/s390/kernel/syscall.c
> +++ b/arch/s390/kernel/syscall.c
> @@ -96,6 +96,7 @@ SYSCALL_DEFINE0(ni_syscall)
>   void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
>   {
>   	unsigned long nr;
> +	bool permit;
>   
>   	enter_from_user_mode_randomize_stack(regs);
>   
> @@ -121,7 +122,9 @@ void noinstr __do_syscall(struct pt_regs
>   		regs->psw.addr = current->restart_block.arch_data;
>   		current->restart_block.arch_data = 1;
>   	}
> -	nr = syscall_enter_from_user_mode_work(regs, nr);
> +
> +	permit = syscall_enter_from_user_mode_work(regs, &nr);
> +
>   	/*
>   	 * In the s390 ptrace ABI, both the syscall number and the return value
>   	 * use gpr2. However, userspace puts the syscall number either in the
> @@ -129,7 +132,7 @@ void noinstr __do_syscall(struct pt_regs
>   	 * work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
>   	 * and if set, the syscall will be skipped.
>   	 */
> -	if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)))
> +	if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET) || !permit))
>   		goto out;
>   	regs->gprs[2] = -ENOSYS;
>   	if (likely(nr < NR_syscalls)) {
> --- a/arch/x86/entry/syscall_32.c
> +++ b/arch/x86/entry/syscall_32.c
> @@ -161,8 +161,9 @@ static __always_inline bool int80_is_ext
>   	nr = syscall_32_enter(regs);
>   
>   	local_irq_enable();
> -	nr = syscall_enter_from_user_mode_work(regs, nr);
> -	do_syscall_32_irqs_on(regs, nr);
> +
> +	if (likely(syscall_enter_from_user_mode_work(regs, &nr)))
> +		do_syscall_32_irqs_on(regs, nr);
>   
>   	instrumentation_end();
>   	syscall_exit_to_user_mode(regs);
> @@ -223,8 +224,8 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
>   	nr = syscall_32_enter(regs);
>   
>   	local_irq_enable();
> -	nr = syscall_enter_from_user_mode_work(regs, nr);
> -	do_syscall_32_irqs_on(regs, nr);
> +	if (likely(syscall_enter_from_user_mode_work(regs, &nr)))
> +		do_syscall_32_irqs_on(regs, nr);
>   
>   	instrumentation_end();
>   	syscall_exit_to_user_mode(regs);
> @@ -243,13 +244,13 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
>   	 * orig_ax, the int return value truncates it. This matches
>   	 * the semantics of syscall_get_nr().
>   	 */
> -	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
> -
> -	instrumentation_begin();
> +	if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
> +		instrumentation_begin();
>   
> -	do_syscall_32_irqs_on(regs, nr);
> +		do_syscall_32_irqs_on(regs, nr);
>   
> -	instrumentation_end();
> +		instrumentation_end();
> +	}
>   	syscall_exit_to_user_mode(regs);
>   }
>   #endif /* !CONFIG_IA32_EMULATION */
> @@ -286,10 +287,8 @@ static noinstr bool __do_fast_syscall_32
>   		return false;
>   	}
>   
> -	nr = syscall_enter_from_user_mode_work(regs, nr);
> -
> -	/* Now this is just like a normal syscall. */
> -	do_syscall_32_irqs_on(regs, nr);
> +	if (likely(syscall_enter_from_user_mode_work(regs, &nr)))
> +		do_syscall_32_irqs_on(regs, nr);
>   
>   	instrumentation_end();
>   	syscall_exit_to_user_mode(regs);
> --- a/arch/x86/entry/syscall_64.c
> +++ b/arch/x86/entry/syscall_64.c
> @@ -78,14 +78,14 @@ static __always_inline void do_syscall_x
>   /* Returns true to return using SYSRET, or false to use IRET */
>   __visible noinstr bool do_syscall_64(struct pt_regs *regs, long nr)
>   {
> -	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
> +	if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
> +		instrumentation_begin();
>   
> -	instrumentation_begin();
> +		if (!do_syscall_x64(regs, nr))
> +			do_syscall_x32(regs, nr);
>   
> -	if (!do_syscall_x64(regs, nr))
> -		do_syscall_x32(regs, nr);
> -
> -	instrumentation_end();
> +		instrumentation_end();
> +	}
>   	syscall_exit_to_user_mode(regs);
>   
>   	/*
> --- a/include/linux/entry-common.h
> +++ b/include/linux/entry-common.h
> @@ -71,7 +71,7 @@ static inline void syscall_enter_audit(s
>   	}
>   }
>   
> -static __always_inline bool syscall_trace_enter(struct pt_regs *regs, unsigned long work,
> +static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work,
>   						long *syscall)
>   {
>   	/*
> @@ -141,16 +141,14 @@ static __always_inline bool syscall_trac
>    *     ptrace_report_syscall_permit_entry(), __seccomp_permit_syscall(), trace_sys_enter()
>    *  2) Invocation of audit_syscall_entry()
>    */
> -static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall)
> +static __always_inline bool syscall_enter_from_user_mode_work(struct pt_regs *regs, long *syscall)
>   {
>   	unsigned long work = READ_ONCE(current_thread_info()->syscall_work);
>   
> -	if (work & SYSCALL_WORK_ENTER) {
> -		if (!syscall_trace_enter(regs, work, &syscall))
> -			return -1L;
> -	}
> +	if (unlikely(work & SYSCALL_WORK_ENTER))
> +		return syscall_trace_enter(regs, work, syscall);
>   
> -	return syscall;
> +	return true;
>   }
>   
>   /**
> 



^ permalink raw reply related

* Re: [PATCH 1/1] powerpc/eeh: Prevent EEH false positives on PMCSR reads in D3cold
From: Vaibhav Jain @ 2026-07-08  4:42 UTC (permalink / raw)
  To: Narayana Murty N, mahesh, mpe, maddy
  Cc: oohall, npiggin, chleroy, nnmlinux, linuxppc-dev, linux-kernel,
	sbhat, harshpb
In-Reply-To: <20260703033649.41633-1-nnmlinux@linux.ibm.com>

Hi Narayana,

Thanks for the patch. My review comments inline below:
Narayana Murty N <nnmlinux@linux.ibm.com> writes:

> On pseries systems, RTAS-based PCI config space reads can trigger
> false EEH events when attempting to read the Power Management Control
> Status Register (PMCSR) while a device is in D3cold state. This occurs
> because the device is powered off and cannot respond to config space
> accesses, causing the platform to report an error condition.
Can you share more details of the test-case(s) in which PMCSR register
read is triggered while the PCI device is in D3Cold state.


>
> This patch addresses the issue by:
>
> 1. Caching PM capability information in struct eeh_dev:
>    - pm_cap: Offset of the PM capability structure
>    - pmcsr_offset: Absolute config space offset of PMCSR register
>
> 2. Synchronizing PM capability data between pci_dev and eeh_dev:
>    - During device probe (eeh_probe_device), copy pm_cap from pci_dev
>    - During early init (pseries_eeh_init_edev), discover pm_cap via
>      firmware before pci_dev exists
>
> 3. Intercepting PMCSR reads in D3cold state:
>    - In rtas_pci_dn_write_config, detect PMCSR read attempts
>    - Check if device is in D3cold via pdev->current_state
>    - Return synthetic success value instead of performing RTAS call
>    - Prevents hardware access that would trigger false EEH event
>
> The fix handles both early boot (before pci_dev exists) and runtime
> scenarios, ensuring PM capability information is always available when
> needed. By blocking RTAS reads to PMCSR when devices are in D3cold,
> we prevent spurious EEH events while maintaining proper error detection
> for genuine hardware failures.

The patch is adding extra branch in rtas_pci_dn_write_config() which is
the hot path for PCI device config space access. Can you share details
on the extra overhead the patch introduces in rtas_pci_dn_write_config()

That being said:
I prefer access to PMCSR being fixed at the call sites which may be
trigerring EEH you have mentioned above instead of adding a constant
overhead to rtas_pci_dn_write_config() 

>
> Signed-off-by: Narayana Murty N <nnmlinux@linux.ibm.com>
> ---
>  arch/powerpc/include/asm/eeh.h               |  9 +++++++
>  arch/powerpc/kernel/eeh.c                    | 16 ++++++++++++
>  arch/powerpc/kernel/rtas_pci.c               | 26 ++++++++++++++++++++
>  arch/powerpc/platforms/pseries/eeh_pseries.c | 16 ++++++++++++
>  4 files changed, 67 insertions(+)
>
> diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
> index b7ebb4ac2c71..224a3adcd34e 100644
> --- a/arch/powerpc/include/asm/eeh.h
> +++ b/arch/powerpc/include/asm/eeh.h
> @@ -139,6 +139,15 @@ struct eeh_dev {
>  	int pcie_cap;			/* Saved PCIe capability	*/
>  	int aer_cap;			/* Saved AER capability		*/
>  	int af_cap;			/* Saved AF capability		*/
> +	/*
> +	 * Cached PCI PM capability information.
> +	 * pm_cap == 0 means the device does not have PCI PM capability
> +	 * or it has not been discovered yet.
> +	 * pmcsr_offset is the absolute config-space offset of PMCSR:
> +	 *     pm_cap + PCI_PM_CTRL
> +	 */
> +	u8 pm_cap;
> +	u16 pmcsr_offset;
>  	struct eeh_pe *pe;		/* Associated PE		*/
>  	struct list_head entry;		/* Membership in eeh_pe.edevs	*/
>  	struct list_head rmv_entry;	/* Membership in rmv_list	*/
> diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
> index bb836f02101c..4402166df8c0 100644
> --- a/arch/powerpc/kernel/eeh.c
> +++ b/arch/powerpc/kernel/eeh.c
> @@ -997,6 +997,21 @@ int eeh_init(struct eeh_ops *ops)
>  	return eeh_event_init();
>  }
>  
> +#ifdef CONFIG_EEH
> +static void eeh_sync_pm_cap(struct eeh_dev *edev, struct pci_dev *pdev)
> +{
> +	if (!edev || !pdev)
> +		return;
> +
> +	/*
> +	 * Prefer PCI core cached PM capability once pci_dev exists.
> +	 * If pm_cap is zero, clear pmcsr_offset as well.
> +	 */
> +	edev->pm_cap = pdev->pm_cap;
> +	edev->pmcsr_offset = pdev->pm_cap ? pdev->pm_cap + PCI_PM_CTRL : 0;
> +}
> +#endif
> +
>  /**
>   * eeh_probe_device() - Perform EEH initialization for the indicated pci device
>   * @dev: pci device for which to set up EEH
> @@ -1050,6 +1065,7 @@ void eeh_probe_device(struct pci_dev *dev)
>  	/* bind the pdev and the edev together */
>  	edev->pdev = dev;
>  	dev->dev.archdata.edev = edev;
> +	eeh_sync_pm_cap(edev, dev);
>  	eeh_addr_cache_insert_dev(dev);
>  	eeh_sysfs_add_device(dev);
>  }
> diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c
> index fccf96e897f6..9668cd0411e1 100644
> --- a/arch/powerpc/kernel/rtas_pci.c
> +++ b/arch/powerpc/kernel/rtas_pci.c
> @@ -95,6 +95,24 @@ static int rtas_pci_read_config(struct pci_bus *bus,
>  	return ret;
>  }
>  
> +static bool eeh_handle_pmcsr_read(struct eeh_dev *edev, int size,
> +				  u32 *val, int *pcibios_ret)
> +{
> +	struct pci_dev *pdev;
> +
> +	if (!edev)
> +		return false;
> +
> +	pdev = edev->pdev;
> +	if (!pdev || pdev->current_state != PCI_D3cold)
> +		return false;
> +
> +	*val = EEH_IO_ERROR_VALUE(size);
> +	*pcibios_ret = PCIBIOS_SUCCESSFUL;
> +
> +	return true;
> +}
> +
>  int rtas_pci_dn_write_config(struct pci_dn *pdn, int where, int size, u32 val)
>  {
>  	unsigned long buid, addr;
> @@ -108,6 +126,14 @@ int rtas_pci_dn_write_config(struct pci_dn *pdn, int where, int size, u32 val)
>  	if (pdn->edev && pdn->edev->pe &&
>  	    (pdn->edev->pe->state & EEH_PE_CFG_BLOCKED))
>  		return PCIBIOS_SET_FAILED;
> +
> +	if (unlikely(pdn->edev && pdn->edev->pmcsr_offset &&
> +		     size == 2 && where == pdn->edev->pmcsr_offset)) {
> +		int pcibios_ret;
> +
> +		if (eeh_handle_pmcsr_read(pdn->edev, size, &val, &pcibios_ret))
> +			return pcibios_ret;
> +	}
>  #endif
>  
>  	addr = rtas_config_addr(pdn->busno, pdn->devfn, where);
> diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
> index b12ef382fec7..0e7efb4bf2d4 100644
> --- a/arch/powerpc/platforms/pseries/eeh_pseries.c
> +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
> @@ -351,6 +351,21 @@ static struct eeh_pe *pseries_eeh_pe_get_parent(struct eeh_dev *edev)
>  	return NULL;
>  }
>  
> +static void pseries_eeh_init_pm_cap(struct pci_dn *pdn, struct eeh_dev *edev)
> +{
> +	edev->pm_cap = 0;
> +	edev->pmcsr_offset = 0;
> +
> +	if (!pdn || !edev)
> +		return;
> +
> +	edev->pm_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_PM);
> +	if (!edev->pm_cap)
> +		return;
> +
> +	edev->pmcsr_offset = edev->pm_cap + PCI_PM_CTRL;
> +}
> +
>  /**
>   * pseries_eeh_init_edev - initialise the eeh_dev and eeh_pe for a pci_dn
>   *
> @@ -408,6 +423,7 @@ static void pseries_eeh_init_edev(struct pci_dn *pdn)
>  	edev->pcix_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_PCIX);
>  	edev->pcie_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_EXP);
>  	edev->aer_cap = pseries_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR);
> +	pseries_eeh_init_pm_cap(pdn, edev);
>  	edev->mode &= 0xFFFFFF00;
>  	if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {
>  		edev->mode |= EEH_DEV_BRIDGE;
> -- 
> 2.51.1
>
>

-- 
Cheers
~ Vaibhav


^ permalink raw reply

* Re: [PATCH] powerpc/970: fix nap return address corruption on async interrupt exit
From: Shrikanth Hegde @ 2026-07-08  4:27 UTC (permalink / raw)
  To: Mukesh Kumar Chaurasiya (IBM), maddy, mpe, npiggin, chleroy,
	mchauras, ruanjinjie, thuth, linuxppc-dev, linux-kernel
  Cc: Andreas Schwab
In-Reply-To: <20260707172430.790040-1-mkchauras@gmail.com>

Hi Mukesh.

On 7/7/26 10:54 PM, Mukesh Kumar Chaurasiya (IBM) wrote:
> On PowerMac G5 (PPC970, CONFIG_PPC_970_NAP) the system panics shortly
> after boot with symptoms including instruction fetch faults, kernel data
> access faults, and stack corruption, predominantly on SMP and always
> somewhere inside softirq processing.
> 
> The PPC970 idle path works by setting _TLF_NAPPING in the current
> thread's local flags before entering the MSR_POW nap loop.  When any
> async interrupt wakes the CPU, nap_adjust_return() is expected to detect
> _TLF_NAPPING, clear it, and rewrite regs->NIP to power4_idle_nap_return
> so that the interrupt returns cleanly to the caller of power4_idle_nap()
> rather than back into the nap spin loop.
> 
> DEFINE_INTERRUPT_HANDLER_ASYNC generates the following sequence:
> 
>      irq_enter_rcu();
>      ____func(regs);           /* timer_interrupt / do_IRQ body */
>      irq_exit_rcu();           /* softirqs run here, irqs re-enabled */
>      arch_interrupt_async_exit_prepare(regs); /* nap_adjust_return was here */
>      irqentry_exit(regs, state);
> 
> irq_exit_rcu() calls invoke_softirq() -> do_softirq_own_stack(), which
> runs softirqs with hardware interrupts re-enabled.  A nested async
> interrupt can therefore arrive while _TLF_NAPPING is still set.  That
> nested interrupt reaches nap_adjust_return() in its own
> arch_interrupt_async_exit_prepare() call, finds _TLF_NAPPING set, and
> redirects *its own* regs->NIP to power4_idle_nap_return.  Returning via
> that blr with an unrelated LR on the softirq stack jumps to a garbage
> address, causing the observed crashes.
> 
> The comment that previously lived in arch_interrupt_async_exit_prepare()
> even described this exact hazard ("must come before irq_exit()"), but
> nap_adjust_return() was placed after irq_exit_rcu() in the macro, so
> the protection was never effective.
> 
> Fix this by calling nap_adjust_return() inside DEFINE_INTERRUPT_HANDLER_ASYNC
> immediately before irq_exit_rcu(), ensuring _TLF_NAPPING is cleared and
> regs->NIP is adjusted before any code that can re-enable interrupts or
> invoke softirqs runs.  Move the explanatory comment into
> nap_adjust_return() itself and remove it from arch_interrupt_async_exit_prepare().
> 
> Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature")
> Closes: https://lore.kernel.org/all/87wlvazrdy.fsf@igel.home/
> Reported-by: Andreas Schwab <schwab@linux-m68k.org>
> Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> ---
>   arch/powerpc/include/asm/entry-common.h | 15 +++++++--------
>   arch/powerpc/include/asm/interrupt.h    |  1 +
>   2 files changed, 8 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/entry-common.h b/arch/powerpc/include/asm/entry-common.h
> index fc636c42e89a..c5adb5006361 100644
> --- a/arch/powerpc/include/asm/entry-common.h
> +++ b/arch/powerpc/include/asm/entry-common.h
> @@ -66,6 +66,13 @@ static inline void srr_regs_clobbered(void)
>   static inline void nap_adjust_return(struct pt_regs *regs)
>   {
>   #ifdef CONFIG_PPC_970_NAP
> +	/*
> +	 * Adjust the nap return address before irq_exit_rcu(). irq_exit_rcu()
> +	 * may invoke softirqs with interrupts re-enabled, allowing a nested
> +	 * async interrupt to arrive. If _TLF_NAPPING is still set at that
> +	 * point, the nested interrupt would erroneously redirect its own
> +	 * return address to power4_idle_nap_return, corrupting the stack.
> +	 */
>   	if (unlikely(test_thread_local_flags(_TLF_NAPPING))) {
>   		/* Can avoid a test-and-clear because NMIs do not call this */
>   		clear_thread_local_flags(_TLF_NAPPING);
> @@ -286,14 +293,6 @@ static inline void arch_interrupt_async_enter_prepare(struct pt_regs *regs)
>   
>   static inline void arch_interrupt_async_exit_prepare(struct pt_regs *regs)
>   {
> -	/*
> -	 * Adjust at exit so the main handler sees the true NIA. This must
> -	 * come before irq_exit() because irq_exit can enable interrupts, and
> -	 * if another interrupt is taken before nap_adjust_return has run
> -	 * here, then that interrupt would return directly to idle nap return.
> -	 */
> -	nap_adjust_return(regs);
> -
>   	arch_interrupt_exit_prepare(regs);
>   }

This makes arch_interrupt_async_exit_prepare same as arch_interrupt_exit_prepare.
Maybe remove arch_interrupt_async_exit_prepare?

>   
> diff --git a/arch/powerpc/include/asm/interrupt.h b/arch/powerpc/include/asm/interrupt.h
> index fb42a664ae54..1b45a49e9bed 100644
> --- a/arch/powerpc/include/asm/interrupt.h
> +++ b/arch/powerpc/include/asm/interrupt.h
> @@ -246,6 +246,7 @@ interrupt_handler void func(struct pt_regs *regs)			\
>   	instrumentation_begin();					\
>   	irq_enter_rcu();						\
>   	____##func (regs);						\
> +	nap_adjust_return(regs);					\
>   	irq_exit_rcu();							\
>   	instrumentation_end();						\
>   	arch_interrupt_async_exit_prepare(regs);			\



^ permalink raw reply

* [powerpc:merge] BUILD SUCCESS f9955df41cbe85e84f79e0b59fd91f4f1dc78a29
From: kernel test robot @ 2026-07-08  2:48 UTC (permalink / raw)
  To: Madhavan Srinivasan; +Cc: linuxppc-dev

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git merge
branch HEAD: f9955df41cbe85e84f79e0b59fd91f4f1dc78a29  Automatic merge of 'fixes' into merge (2026-07-07 16:13)

elapsed time: 936m

configs tested: 192
configs skipped: 2

The following configs have been built successfully.
More configs may be tested in the coming days.

tested configs:
alpha                             allnoconfig    gcc-16.1.0
alpha                            allyesconfig    gcc-16.1.0
alpha                               defconfig    gcc-16.1.0
arc                              alldefconfig    gcc-16.1.0
arc                              allmodconfig    clang-23
arc                              allmodconfig    gcc-16.1.0
arc                               allnoconfig    gcc-16.1.0
arc                              allyesconfig    clang-23
arc                              allyesconfig    gcc-16.1.0
arc                                 defconfig    gcc-16.1.0
arc                   randconfig-001-20260708    gcc-13.4.0
arc                   randconfig-002-20260708    gcc-13.4.0
arm                               allnoconfig    clang-17
arm                               allnoconfig    gcc-16.1.0
arm                              allyesconfig    clang-23
arm                                 defconfig    gcc-16.1.0
arm                   randconfig-001-20260708    gcc-13.4.0
arm                   randconfig-002-20260708    gcc-13.4.0
arm                   randconfig-003-20260708    gcc-13.4.0
arm                   randconfig-004-20260708    gcc-13.4.0
arm64                            allmodconfig    clang-23
arm64                             allnoconfig    gcc-16.1.0
arm64                               defconfig    gcc-16.1.0
arm64                 randconfig-001-20260708    gcc-9.5.0
arm64                 randconfig-002-20260708    gcc-9.5.0
arm64                 randconfig-003-20260708    gcc-9.5.0
arm64                 randconfig-004-20260708    gcc-9.5.0
csky                             allmodconfig    gcc-16.1.0
csky                              allnoconfig    gcc-16.1.0
csky                                defconfig    gcc-16.1.0
csky                  randconfig-001-20260708    gcc-9.5.0
csky                  randconfig-002-20260708    gcc-9.5.0
hexagon                          allmodconfig    clang-23
hexagon                          allmodconfig    gcc-16.1.0
hexagon                           allnoconfig    clang-23
hexagon                           allnoconfig    gcc-16.1.0
hexagon                             defconfig    gcc-16.1.0
hexagon               randconfig-001-20260708    gcc-13.4.0
hexagon               randconfig-002-20260708    gcc-13.4.0
i386                             allmodconfig    clang-22
i386                              allnoconfig    gcc-14
i386                              allnoconfig    gcc-16.1.0
i386                             allyesconfig    clang-22
i386        buildonly-randconfig-001-20260708    clang-22
i386        buildonly-randconfig-002-20260708    clang-22
i386        buildonly-randconfig-003-20260708    clang-22
i386        buildonly-randconfig-004-20260708    clang-22
i386        buildonly-randconfig-005-20260708    clang-22
i386        buildonly-randconfig-006-20260708    clang-22
i386                                defconfig    gcc-16.1.0
i386                  randconfig-001-20260708    clang-22
i386                  randconfig-002-20260708    clang-22
i386                  randconfig-003-20260708    clang-22
i386                  randconfig-004-20260708    clang-22
i386                  randconfig-005-20260708    clang-22
i386                  randconfig-006-20260708    clang-22
i386                  randconfig-007-20260708    clang-22
i386                  randconfig-011-20260708    gcc-14
i386                  randconfig-012-20260708    gcc-14
i386                  randconfig-013-20260708    gcc-14
i386                  randconfig-014-20260708    gcc-14
i386                  randconfig-015-20260708    gcc-14
i386                  randconfig-016-20260708    gcc-14
i386                  randconfig-017-20260708    gcc-14
loongarch                        allmodconfig    clang-19
loongarch                        allmodconfig    clang-23
loongarch                         allnoconfig    clang-20
loongarch                         allnoconfig    gcc-16.1.0
loongarch                           defconfig    clang-23
loongarch             randconfig-001-20260708    gcc-13.4.0
loongarch             randconfig-002-20260708    gcc-13.4.0
m68k                             allmodconfig    gcc-16.1.0
m68k                              allnoconfig    gcc-16.1.0
m68k                             allyesconfig    clang-23
m68k                             allyesconfig    gcc-16.1.0
m68k                                defconfig    clang-23
microblaze                        allnoconfig    gcc-16.1.0
microblaze                       allyesconfig    gcc-16.1.0
microblaze                          defconfig    clang-23
mips                             allmodconfig    gcc-16.1.0
mips                              allnoconfig    gcc-16.1.0
mips                             allyesconfig    gcc-16.1.0
nios2                            allmodconfig    clang-20
nios2                            allmodconfig    gcc-11.5.0
nios2                             allnoconfig    clang-23
nios2                             allnoconfig    gcc-11.5.0
nios2                               defconfig    clang-23
nios2                 randconfig-001-20260708    gcc-13.4.0
nios2                 randconfig-002-20260708    gcc-13.4.0
openrisc                         allmodconfig    clang-20
openrisc                         allmodconfig    gcc-16.1.0
openrisc                          allnoconfig    clang-23
openrisc                          allnoconfig    gcc-16.1.0
openrisc                            defconfig    gcc-16.1.0
parisc                           allmodconfig    gcc-16.1.0
parisc                            allnoconfig    clang-23
parisc                            allnoconfig    gcc-16.1.0
parisc                           allyesconfig    clang-17
parisc                              defconfig    gcc-16.1.0
parisc                randconfig-001-20260708    clang-22
parisc                randconfig-002-20260708    clang-22
parisc64                            defconfig    clang-23
powerpc                          allmodconfig    gcc-16.1.0
powerpc                           allnoconfig    clang-23
powerpc                           allnoconfig    gcc-16.1.0
powerpc               randconfig-001-20260708    clang-22
powerpc               randconfig-002-20260708    clang-22
powerpc64             randconfig-001-20260708    clang-22
powerpc64             randconfig-002-20260708    clang-22
riscv                            allmodconfig    clang-23
riscv                             allnoconfig    clang-23
riscv                             allnoconfig    gcc-16.1.0
riscv                            allyesconfig    clang-23
riscv                               defconfig    gcc-16.1.0
riscv                 randconfig-001-20260708    clang-23
riscv                 randconfig-002-20260708    clang-23
s390                             allmodconfig    clang-17
s390                              allnoconfig    clang-23
s390                             allyesconfig    gcc-16.1.0
s390                                defconfig    gcc-16.1.0
s390                  randconfig-001-20260708    clang-23
s390                  randconfig-002-20260708    clang-23
sh                               allmodconfig    gcc-16.1.0
sh                                allnoconfig    clang-23
sh                                allnoconfig    gcc-16.1.0
sh                               allyesconfig    clang-17
sh                               allyesconfig    gcc-16.1.0
sh                                  defconfig    gcc-14
sh                    randconfig-001-20260708    clang-23
sh                    randconfig-002-20260708    clang-23
sparc                             allnoconfig    clang-23
sparc                             allnoconfig    gcc-16.1.0
sparc                               defconfig    gcc-16.1.0
sparc                 randconfig-001-20260708    gcc-8.5.0
sparc                 randconfig-002-20260708    gcc-8.5.0
sparc64                          allmodconfig    clang-20
sparc64                             defconfig    gcc-14
sparc64               randconfig-001-20260708    gcc-8.5.0
sparc64               randconfig-002-20260708    gcc-8.5.0
um                               allmodconfig    clang-17
um                                allnoconfig    clang-17
um                                allnoconfig    clang-23
um                               allyesconfig    gcc-14
um                               allyesconfig    gcc-16.1.0
um                                  defconfig    gcc-14
um                             i386_defconfig    gcc-14
um                    randconfig-001-20260708    gcc-8.5.0
um                    randconfig-002-20260708    gcc-8.5.0
um                           x86_64_defconfig    gcc-14
x86_64                           allmodconfig    clang-22
x86_64                            allnoconfig    clang-22
x86_64                            allnoconfig    clang-23
x86_64                           allyesconfig    clang-22
x86_64      buildonly-randconfig-001-20260708    gcc-12
x86_64      buildonly-randconfig-002-20260708    gcc-12
x86_64      buildonly-randconfig-003-20260708    gcc-12
x86_64      buildonly-randconfig-004-20260708    gcc-12
x86_64      buildonly-randconfig-005-20260708    gcc-12
x86_64      buildonly-randconfig-006-20260708    gcc-12
x86_64                              defconfig    gcc-14
x86_64                                  kexec    clang-22
x86_64                randconfig-001-20260708    gcc-14
x86_64                randconfig-002-20260708    gcc-14
x86_64                randconfig-003-20260708    gcc-14
x86_64                randconfig-004-20260708    gcc-14
x86_64                randconfig-005-20260708    gcc-14
x86_64                randconfig-006-20260708    gcc-14
x86_64                randconfig-011-20260708    clang-22
x86_64                randconfig-012-20260708    clang-22
x86_64                randconfig-013-20260708    clang-22
x86_64                randconfig-014-20260708    clang-22
x86_64                randconfig-015-20260708    clang-22
x86_64                randconfig-016-20260708    clang-22
x86_64                randconfig-071-20260708    clang-22
x86_64                randconfig-072-20260708    clang-22
x86_64                randconfig-073-20260708    clang-22
x86_64                randconfig-074-20260708    clang-22
x86_64                randconfig-075-20260708    clang-22
x86_64                randconfig-076-20260708    clang-22
x86_64                               rhel-9.4    clang-22
x86_64                           rhel-9.4-bpf    gcc-14
x86_64                          rhel-9.4-func    clang-22
x86_64                    rhel-9.4-kselftests    clang-22
x86_64                         rhel-9.4-kunit    gcc-14
x86_64                           rhel-9.4-ltp    gcc-14
x86_64                          rhel-9.4-rust    clang-22
xtensa                            allnoconfig    clang-23
xtensa                            allnoconfig    gcc-16.1.0
xtensa                           allyesconfig    clang-20
xtensa                           allyesconfig    gcc-16.1.0
xtensa                randconfig-001-20260708    gcc-8.5.0
xtensa                randconfig-002-20260708    gcc-8.5.0

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply

* Re: [PATCH 08/13] mm: introduce vma_get_page_prot() and use it
From: Zi Yan @ 2026-07-08  2:36 UTC (permalink / raw)
  To: Lorenzo Stoakes, Andrew Morton
  Cc: Thomas Bogendoerfer, Madhavan Srinivasan, Michael Ellerman,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Lucas Stach, Inki Dae, Seung-Woo Kim,
	Kyungmin Park, Krzysztof Kozlowski, Peter Griffin, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin, Rob Clark,
	Dmitry Baryshkov, Lyude Paul, Danilo Krummrich, Tomi Valkeinen,
	Sandy Huang, Heiko Stübner, Andy Yan, Thierry Reding,
	Mikko Perttunen, Jonathan Hunter, Gerd Hoffmann, Dmitry Osipenko,
	Zack Rusin, Matthew Brost, Thomas Hellstrom,
	Oleksandr Andrushchenko, Helge Deller, Benjamin LaHaise,
	Alexander Viro, Christian Brauner, Muchun Song, Oscar Salvador,
	David Hildenbrand, Baolin Wang, Liam R . Howlett, Nico Pache,
	Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Hugh Dickins,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Jann Horn, Pedro Falcato, Kees Cook, Jaroslav Kysela,
	Takashi Iwai, linux-mips, linux-kernel, linuxppc-dev, dri-devel,
	etnaviv, linux-arm-kernel, linux-samsung-soc, intel-gfx,
	linux-arm-msm, freedreno, nouveau, linux-rockchip, linux-tegra,
	virtualization, intel-xe, xen-devel, linux-fbdev, linux-aio,
	linux-fsdevel, linux-mm, linux-sound
In-Reply-To: <3bb8bdc4788230c33102166d56cbc5abfad9d4cb.1782760670.git.ljs@kernel.org>

On Mon Jun 29, 2026 at 3:25 PM EDT, Lorenzo Stoakes wrote:
> There's a large number of vm_get_page_prot(vma->vm_flags) invocations. Make
> life easier by introducing vma_get_page_prot() parameterised by the VMA.
>
> This also makes converting vm_get_page_prot() to vma_flags_t easier.
>
> Also update the userland VMA tests to reflect the change.
>
> No functional change intended.
>
> Signed-off-by: Lorenzo Stoakes <ljs@kernel.org>
> ---
>  drivers/gpu/drm/drm_gem.c                   |  2 +-
>  drivers/gpu/drm/drm_gem_dma_helper.c        |  2 +-
>  drivers/gpu/drm/drm_gem_shmem_helper.c      |  2 +-
>  drivers/gpu/drm/etnaviv/etnaviv_gem.c       |  2 +-
>  drivers/gpu/drm/exynos/exynos_drm_gem.c     |  6 +++---
>  drivers/gpu/drm/i915/gem/i915_gem_mman.c    | 12 ++++++------
>  drivers/gpu/drm/msm/msm_gem.c               |  2 +-
>  drivers/gpu/drm/nouveau/nouveau_gem.c       |  2 +-
>  drivers/gpu/drm/omapdrm/omap_fbdev.c        |  2 +-
>  drivers/gpu/drm/omapdrm/omap_gem.c          |  6 +++---
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.c |  2 +-
>  drivers/gpu/drm/tegra/gem.c                 |  2 +-
>  drivers/gpu/drm/virtio/virtgpu_vram.c       |  2 +-
>  drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c  |  2 +-
>  drivers/gpu/drm/xe/xe_device.c              |  2 +-
>  drivers/gpu/drm/xe/xe_mmio_gem.c            |  2 +-
>  drivers/gpu/drm/xen/xen_drm_front_gem.c     |  2 +-
>  drivers/video/fbdev/core/fb_io_fops.c       |  2 +-
>  include/linux/mm.h                          | 10 +++++++++-
>  mm/vma.c                                    |  2 +-
>  mm/vma_exec.c                               |  2 +-
>  sound/core/memalloc.c                       |  2 +-
>  tools/testing/vma/include/dup.h             |  4 ++++
>  23 files changed, 43 insertions(+), 31 deletions(-)
>

Acked-by: Zi Yan <ziy@nvidia.com>

-- 
Best Regards,
Yan, Zi



^ permalink raw reply

* Re: [PATCH 07/13] mm/vma: rename vma_get_page_prot to vma_flags_to_page_prot
From: Zi Yan @ 2026-07-08  2:31 UTC (permalink / raw)
  To: Lorenzo Stoakes, Andrew Morton
  Cc: Thomas Bogendoerfer, Madhavan Srinivasan, Michael Ellerman,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Lucas Stach, Inki Dae, Seung-Woo Kim,
	Kyungmin Park, Krzysztof Kozlowski, Peter Griffin, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin, Rob Clark,
	Dmitry Baryshkov, Lyude Paul, Danilo Krummrich, Tomi Valkeinen,
	Sandy Huang, Heiko Stübner, Andy Yan, Thierry Reding,
	Mikko Perttunen, Jonathan Hunter, Gerd Hoffmann, Dmitry Osipenko,
	Zack Rusin, Matthew Brost, Thomas Hellstrom,
	Oleksandr Andrushchenko, Helge Deller, Benjamin LaHaise,
	Alexander Viro, Christian Brauner, Muchun Song, Oscar Salvador,
	David Hildenbrand, Baolin Wang, Liam R . Howlett, Nico Pache,
	Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Hugh Dickins,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Jann Horn, Pedro Falcato, Kees Cook, Jaroslav Kysela,
	Takashi Iwai, linux-mips, linux-kernel, linuxppc-dev, dri-devel,
	etnaviv, linux-arm-kernel, linux-samsung-soc, intel-gfx,
	linux-arm-msm, freedreno, nouveau, linux-rockchip, linux-tegra,
	virtualization, intel-xe, xen-devel, linux-fbdev, linux-aio,
	linux-fsdevel, linux-mm, linux-sound
In-Reply-To: <fc8ac30d03d29d236e76542b36432bba315aca60.1782760670.git.ljs@kernel.org>

On Mon Jun 29, 2026 at 3:25 PM EDT, Lorenzo Stoakes wrote:
> Having vma_get_page_prot() refer to VMA flags and vma_set_page_prot() refer
> to a VMA is confusing.
>
> Rename vma_get_page_prot() to vma_flags_to_page_prot() to resolve this
> confusion.
>
> No functional change intended.
>
> Signed-off-by: Lorenzo Stoakes <ljs@kernel.org>
> ---
>  include/linux/mm.h              | 4 ++--
>  mm/vma.c                        | 2 +-
>  mm/vma.h                        | 2 +-
>  tools/testing/vma/include/dup.h | 2 +-
>  4 files changed, 5 insertions(+), 5 deletions(-)
>
Makes sense.

Reviewed-by: Zi Yan <ziy@nvidia.com>


-- 
Best Regards,
Yan, Zi



^ permalink raw reply

* Re: [PATCH 06/13] mm/vma: convert vm_pgprot_modify() to use vma_flags_t and rename
From: Zi Yan @ 2026-07-08  2:30 UTC (permalink / raw)
  To: Lorenzo Stoakes, Andrew Morton
  Cc: Thomas Bogendoerfer, Madhavan Srinivasan, Michael Ellerman,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Lucas Stach, Inki Dae, Seung-Woo Kim,
	Kyungmin Park, Krzysztof Kozlowski, Peter Griffin, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin, Rob Clark,
	Dmitry Baryshkov, Lyude Paul, Danilo Krummrich, Tomi Valkeinen,
	Sandy Huang, Heiko Stübner, Andy Yan, Thierry Reding,
	Mikko Perttunen, Jonathan Hunter, Gerd Hoffmann, Dmitry Osipenko,
	Zack Rusin, Matthew Brost, Thomas Hellstrom,
	Oleksandr Andrushchenko, Helge Deller, Benjamin LaHaise,
	Alexander Viro, Christian Brauner, Muchun Song, Oscar Salvador,
	David Hildenbrand, Baolin Wang, Liam R . Howlett, Nico Pache,
	Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Hugh Dickins,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Jann Horn, Pedro Falcato, Kees Cook, Jaroslav Kysela,
	Takashi Iwai, linux-mips, linux-kernel, linuxppc-dev, dri-devel,
	etnaviv, linux-arm-kernel, linux-samsung-soc, intel-gfx,
	linux-arm-msm, freedreno, nouveau, linux-rockchip, linux-tegra,
	virtualization, intel-xe, xen-devel, linux-fbdev, linux-aio,
	linux-fsdevel, linux-mm, linux-sound
In-Reply-To: <548ba81b2971734d4d2143237ad1465bd1b2f525.1782760670.git.ljs@kernel.org>

On Mon Jun 29, 2026 at 3:25 PM EDT, Lorenzo Stoakes wrote:
> Update vm_pgprot_modify() to use the new VMA flags type vma_flags_t, and
> rename to vma_pgprot_modify() accordingly.
>
> This is part of the ongoing work to convert vm_flags_t to vma_flags_t, in
> order to eliminate the arbitrary limit of the number of bits in a system
> word on available VMA flags.
>
> Update VMA userland tests accordingly, updating vma_set_page_prot() to no
> longer inline vma_pgprot_modify(), rather we can simply define
> vma_pgprot_modify() as a static inline function and the tests will pick it
> up from vma.h.
>
> No functional change intended.
>
> Signed-off-by: Lorenzo Stoakes <ljs@kernel.org>
> ---
>  mm/mmap.c                       |  8 ++++----
>  mm/vma.c                        |  2 +-
>  mm/vma.h                        |  6 ++++--
>  tools/testing/vma/include/dup.h | 12 +++++-------
>  4 files changed, 14 insertions(+), 14 deletions(-)
>

Reviewed-by: Zi Yan <ziy@nvidia.com>


-- 
Best Regards,
Yan, Zi



^ permalink raw reply

* Re: [PATCH 05/13] mm: prefer mm->def_vma_flags in mm logic
From: Zi Yan @ 2026-07-08  2:06 UTC (permalink / raw)
  To: Lorenzo Stoakes, Andrew Morton
  Cc: Thomas Bogendoerfer, Madhavan Srinivasan, Michael Ellerman,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Lucas Stach, Inki Dae, Seung-Woo Kim,
	Kyungmin Park, Krzysztof Kozlowski, Peter Griffin, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin, Rob Clark,
	Dmitry Baryshkov, Lyude Paul, Danilo Krummrich, Tomi Valkeinen,
	Sandy Huang, Heiko Stübner, Andy Yan, Thierry Reding,
	Mikko Perttunen, Jonathan Hunter, Gerd Hoffmann, Dmitry Osipenko,
	Zack Rusin, Matthew Brost, Thomas Hellstrom,
	Oleksandr Andrushchenko, Helge Deller, Benjamin LaHaise,
	Alexander Viro, Christian Brauner, Muchun Song, Oscar Salvador,
	David Hildenbrand, Baolin Wang, Liam R . Howlett, Nico Pache,
	Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Hugh Dickins,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Jann Horn, Pedro Falcato, Kees Cook, Jaroslav Kysela,
	Takashi Iwai, linux-mips, linux-kernel, linuxppc-dev, dri-devel,
	etnaviv, linux-arm-kernel, linux-samsung-soc, intel-gfx,
	linux-arm-msm, freedreno, nouveau, linux-rockchip, linux-tegra,
	virtualization, intel-xe, xen-devel, linux-fbdev, linux-aio,
	linux-fsdevel, linux-mm, linux-sound
In-Reply-To: <3b4ccdc38819b42ddc79ee5a795831208ac7986c.1782760670.git.ljs@kernel.org>

On Mon Jun 29, 2026 at 3:25 PM EDT, Lorenzo Stoakes wrote:
> Currently mm->def_flags (of type vm_flags_t) is union'd with
> mm->def_vma_flags (of type vma_flags_t).
>
> As part of the effort to convert vm_flags_t usage to vma_flags_t (in order
> to no longer be arbitrarily limited to a system word size for VMA flags),
> prefer mm->def_vma_flags to mm->def_flags throughout the mm logic.
>
> No functional change intended.
>
> Signed-off-by: Lorenzo Stoakes <ljs@kernel.org>
> ---
>  mm/debug.c |  2 +-
>  mm/mlock.c | 13 +++++++------
>  mm/mmap.c  | 11 ++++++-----
>  mm/vma.c   |  4 ++--
>  4 files changed, 16 insertions(+), 14 deletions(-)
>

LGTM.

Reviewed-by: Zi Yan <ziy@nvidia.com>


-- 
Best Regards,
Yan, Zi



^ permalink raw reply

* [PATCH 2/2] powerpc/pseries: Skip vpa_init() for boot cpu in smp_setup_cpu()
From: Vaibhav Jain @ 2026-07-08  1:58 UTC (permalink / raw)
  To: linuxppc-dev, kvm, kvm-ppc
  Cc: Vaibhav Jain, Madhavan Srinivasan, Michael Ellerman

During pSeries_setup_arch(), VPA for boot-cpu is first to be
initialized. However later in the boot, smp_setup_cpu() is called for
setting up VPA on boot and secondary cpus that were brought online. This
results in vpa_init() being called twice for boot-cpu and three redundant
H_REGISTER_VPA hcalls being made to the hypervisor.

Fix this by adding an extra condition in smp_set_cpu() to call vpa_init()
only on non boot-cpus.

Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
---
 arch/powerpc/platforms/pseries/smp.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c
index c6c2baacca9a..106f79a96edc 100644
--- a/arch/powerpc/platforms/pseries/smp.c
+++ b/arch/powerpc/platforms/pseries/smp.c
@@ -131,7 +131,12 @@ static void smp_setup_cpu(int cpu)
 	else if (cpu != boot_cpuid)
 		xics_setup_cpu();
 
-	if (firmware_has_feature(FW_FEATURE_SPLPAR))
+	/*
+	 * Initialize VPA on non-boot cpus since boot-cpu vpa was
+	 * already initialized in pSeries_setup_arch()
+	 */
+	if (firmware_has_feature(FW_FEATURE_SPLPAR) &&
+	    cpu != boot_cpuid)
 		vpa_init(cpu);
 
 	cpumask_clear_cpu(cpu, of_spin_mask);
-- 
2.55.0



^ permalink raw reply related

* [PATCH 1/2] powerpc/pseries: Ensure vpa,slb_shadow & dtl are unregistered during crash
From: Vaibhav Jain @ 2026-07-08  1:58 UTC (permalink / raw)
  To: linuxppc-dev, kvm, kvm-ppc
  Cc: Vaibhav Jain, Madhavan Srinivasan, Michael Ellerman,
	Anushree Mathur

Currently pseries_kexec_cpu_down() skips unregistering vpa, slb_shadow and
dtl areas during a crash and kexec shutdown path. It was done to avoid
doing an HCALL while crashing. However recently Anushree reported that
during kernel crash while the kdump kernel was coming up, Hypervisor
reported invalid values for 'vpa.yield_count' while it dispatching L2-KVM
Guest vcpus. The error manifested as debug build Hypervisor assert
triggering to indicate possible VPA corruption.

Looking at the kexec cpu offline path it was discovered that during crash
kernel doesn't unregister the VPA/SLB-Shadow/DTL area with
Hypervisor. Instead it re-allocates and re-registers these areas
for cpus during boot. During kexec boot the previously allocated areas
can get overwritten with new content without hypervisor knowledge. This
creates a small window where while kexec kernel boots and the L2-VCPUs are
being dispatched, Hypervisor may try to read/write to a wrong memory area
which previously belonged to older VPA.

Fix this possible race and memory corruption by updating
pseries_kexec_cpu_down() to also unregister vpa,slb_shadow & dtl areas
during a kernel crash.

Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
Tested-by: Anushree Mathur <anushree.mathur@linux.ibm.com>
---
 arch/powerpc/platforms/pseries/kexec.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c
index 431be156ca9b..29f7c97ff193 100644
--- a/arch/powerpc/platforms/pseries/kexec.c
+++ b/arch/powerpc/platforms/pseries/kexec.c
@@ -20,12 +20,15 @@
 void pseries_kexec_cpu_down(int crash_shutdown, int secondary)
 {
 	/*
-	 * Don't risk a hypervisor call if we're crashing
-	 * XXX: Why? The hypervisor is not crashing. It might be better
-	 * to at least attempt unregister to avoid the hypervisor stepping
-	 * on our memory.
+	 * Ensure vpa/slb_shadow/dtl cleanup even while we are crashing.
+	 * Why? The hypervisor is not crashing so at least attempt unregister to
+	 * avoid the hypervisor stepping on our memory. If hypervisor or kexec
+	 * kernel steps on the old memory allocated to these areas before the
+	 * new kexec-kernel happens to allocate and register new areas,
+	 * the hypervisor will see invalid content which may cause
+	 * unexpected behavior.
 	 */
-	if (firmware_has_feature(FW_FEATURE_SPLPAR) && !crash_shutdown) {
+	if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
 		int ret;
 		int cpu = smp_processor_id();
 		int hwcpu = hard_smp_processor_id();
-- 
2.55.0



^ permalink raw reply related

* Re: [patch 11/18] seccomp, treewide: Rename and convert __secure_computing() to return boolean
From: Jinjie Ruan @ 2026-07-08  1:43 UTC (permalink / raw)
  To: Thomas Gleixner, LKML
  Cc: Peter Zijlstra, Mark Rutland, Kees Cook, Andy Lutomirski,
	Oleg Nesterov, Richard Henderson, Russell King, Catalin Marinas,
	Guo Ren, Geert Uytterhoeven, Thomas Bogendoerfer, Helge Deller,
	Yoshinori Sato, Richard Weinberger, Chris Zankel,
	linux-arm-kernel, linux-alpha, linux-csky, linux-m68k, linux-mips,
	linux-parisc, linux-sh, linux-um, Michael Ellerman,
	Shrikanth Hegde, linuxppc-dev, Huacai Chen, loongarch,
	Paul Walmsley, Palmer Dabbelt, linux-riscv, Sven Schnelle,
	linux-s390, x86, Arnd Bergmann, Vineet Gupta, Will Deacon,
	Brian Cain, Michal Simek, Dinh Nguyen, David S. Miller,
	Andreas Larsson, linux-snps-arc, linux-hexagon, linux-openrisc,
	sparclinux, linux-arch, Michal Suchánek, Jonathan Corbet,
	linux-doc
In-Reply-To: <20260707190254.230735780@kernel.org>



On 7/8/2026 3:06 AM, Thomas Gleixner wrote:
> From: Jinjie Ruan <ruanjinjie@huawei.com>
> 
> The return value of __secure_computing() currently uses 0 to indicate
> that a system call should be allowed, and -1 to indicate that it should
> be blocked/killed. This 0/-1 pattern is non-intuitive for a security
> check function and makes the control flow at the call sites less readable.
> 
> Furthermore, any potential future changes to these return values would
> require a high-risk, error-prone audit of all its users across different
> architectures.
> 
> Sanitize this logic by converting the return type of __secure_computing()
> to a proper boolean, where 'true' explicitly means 'allow' and 'false'
> means 'fail/deny'.
> 
> Update all the two dozen or so call sites across the tree to align with
> this new boolean semantic. No functional changes are intended, as the
> callers still return -1 to the lower-level assembly entry code upon
> seccomp denial.
> 
> Rename the function to __seccomp_permit_syscall() so that the purpose is
> entirely clear.
> 
> [ tglx: Rename the function ]
> 
> Suggested-by: Thomas Gleixner <tglx@kernel.org>
> Suggested-by: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: Kees Cook <kees@kernel.org>
> Cc: Andy Lutomirski <luto@kernel.org>
> Cc: Oleg Nesterov <oleg@redhat.com>
> Cc: Richard Henderson <richard.henderson@linaro.org>
> Cc: Russell King <linux@armlinux.org.uk>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Guo Ren <guoren@kernel.org>
> Cc: Geert Uytterhoeven <geert@linux-m68k.org>
> Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
> Cc: Helge Deller <deller@gmx.de>
> Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
> Cc: Richard Weinberger <richard@nod.at>
> Cc: Chris Zankel <chris@zankel.net>
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-alpha@vger.kernel.org
> Cc: linux-csky@vger.kernel.org
> Cc: linux-m68k@lists.linux-m68k.org
> Cc: linux-mips@vger.kernel.org
> Cc: linux-parisc@vger.kernel.org
> Cc: linux-sh@vger.kernel.org
> Cc: linux-um@lists.infradead.org
> ---
>  arch/alpha/kernel/ptrace.c            |    2 -
>  arch/arm/kernel/ptrace.c              |    2 -
>  arch/arm64/kernel/ptrace.c            |    2 -
>  arch/csky/kernel/ptrace.c             |    2 -
>  arch/m68k/kernel/ptrace.c             |    2 -
>  arch/mips/kernel/ptrace.c             |    2 -
>  arch/parisc/kernel/ptrace.c           |    2 -
>  arch/sh/kernel/ptrace_32.c            |    2 -
>  arch/um/kernel/skas/syscall.c         |    2 -
>  arch/x86/entry/vsyscall/vsyscall_64.c |   14 ++++++-------
>  arch/xtensa/kernel/ptrace.c           |    3 --
>  include/linux/entry-common.h          |    9 +++-----
>  include/linux/seccomp.h               |   12 +++++------
>  kernel/seccomp.c                      |   35 +++++++++++++++++-----------------
>  14 files changed, 45 insertions(+), 46 deletions(-)

As Ada pointed out, the description of secure_computing in arch/Kconfig
need to be updated, a possible suggestion:

--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -636,8 +636,8 @@ config HAVE_ARCH_SECCOMP_FILTER
          - syscall_rollback()
          - syscall_set_return_value()
          - SIGSYS siginfo_t support
-         - secure_computing is called from a ptrace_event()-safe context
-         - secure_computing return value is checked and a return value
of -1
+         - seccomp_permits_syscall is called from a ptrace_event()-safe
context
+         - seccomp_permits_syscall return value is checked and if false


Link:
https://lore.kernel.org/all/b8f3b5cd-8d8a-4396-ba0c-011a83234dd9@arm.com/

> --- a/arch/alpha/kernel/ptrace.c
> +++ b/arch/alpha/kernel/ptrace.c
> @@ -387,7 +387,7 @@ asmlinkage unsigned long syscall_trace_e
>  	 * If this fails, seccomp may already have set up the return value
>  	 * (e.g. SECCOMP_RET_ERRNO / TRACE).
>  	 */
> -	if (secure_computing() == -1) {
> +	if (!seccomp_permit_syscall()) {
>  		if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
>  			syscall_set_return_value(current, regs, -ENOSYS, 0);
>  		syscall_set_nr(current, regs, -1);

[...]

> -static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
> +static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace)
>  {
>  	u32 filter_ret, action;
>  	struct seccomp_data sd;
> @@ -1294,7 +1295,7 @@ static int __seccomp_filter(int this_sys
>  	case SECCOMP_RET_TRACE:
>  		/* We've been put in this state by the ptracer already. */
>  		if (recheck_after_trace)
> -			return 0;
> +			return true;
>  
>  		/* ENOSYS these calls if there is no tracer attached. */
>  		if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) {
> @@ -1330,19 +1331,19 @@ static int __seccomp_filter(int this_sys
>  		 * a skip would have already been reported.
>  		 */
>  		if (__seccomp_filter(this_syscall, true))
> -			return -1;
> +			return false;

The return value of __seccomp_filter is checked in the wrong way, check
-1 should be replaced with check false, maybe:

-               if (__seccomp_filter(this_syscall, true))
-                       return -1;
+               if (!__seccomp_filter(this_syscall, true))
+                       return false;

otherwise,

LGTM
Reviewed-by: Jinjie Ruan <ruanjinjie@huawei.com>

>  
> -		return 0;
> +		return true;
>  
>  	case SECCOMP_RET_USER_NOTIF:
>  		if (seccomp_do_user_notification(this_syscall, match, &sd))
>  			goto skip;
>  
> -		return 0;
> +		return true;
>  
>  	case SECCOMP_RET_LOG:
>  		seccomp_log(this_syscall, 0, action, true);
> -		return 0;
> +		return true;
>  
>  	case SECCOMP_RET_ALLOW:
>  		/*
> @@ -1350,7 +1351,7 @@ static int __seccomp_filter(int this_sys
>  		 * this action since SECCOMP_RET_ALLOW is the starting
>  		 * state in seccomp_run_filters().
>  		 */
> -		return 0;
> +		return true;
>  
>  	case SECCOMP_RET_KILL_THREAD:
>  	case SECCOMP_RET_KILL_PROCESS:
> @@ -1367,46 +1368,46 @@ static int __seccomp_filter(int this_sys
>  		} else {
>  			do_exit(SIGSYS);
>  		}
> -		return -1; /* skip the syscall go directly to signal handling */
> +		return false; /* skip the syscall go directly to signal handling */
>  	}
>  
>  	unreachable();
>  
>  skip:
>  	seccomp_log(this_syscall, 0, action, match ? match->log : false);
> -	return -1;
> +	return false;
>  }
>  #else
> -static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
> +static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace)
>  {
>  	BUG();
>  
> -	return -1;
> +	return false;
>  }
>  #endif
>  
> -int __secure_computing(void)
> +bool __seccomp_permit_syscall(void)
>  {
>  	int mode = current->seccomp.mode;
>  	int this_syscall;
>  
>  	if (IS_ENABLED(CONFIG_CHECKPOINT_RESTORE) &&
>  	    unlikely(current->ptrace & PT_SUSPEND_SECCOMP))
> -		return 0;
> +		return true;
>  
>  	this_syscall = syscall_get_nr(current, current_pt_regs());
>  
>  	switch (mode) {
>  	case SECCOMP_MODE_STRICT:
>  		__secure_computing_strict(this_syscall);  /* may call do_exit */
> -		return 0;
> +		return true;
>  	case SECCOMP_MODE_FILTER:
>  		return __seccomp_filter(this_syscall, false);
>  	/* Surviving SECCOMP_RET_KILL_* must be proactively impossible. */
>  	case SECCOMP_MODE_DEAD:
>  		WARN_ON_ONCE(1);
>  		do_exit(SIGKILL);
> -		return -1;
> +		return false;
>  	default:
>  		BUG();
>  	}
> 



^ permalink raw reply

* Re: test_bitmap fails on ppc/ppc64 on kernels v7.1.3, v7.2-rc2
From: Erhard Furtner @ 2026-07-07 23:25 UTC (permalink / raw)
  To: Christophe Leroy (CS GROUP), linuxppc-dev@lists.ozlabs.org,
	Andy Shevchenko
  Cc: linux-kernel
In-Reply-To: <98d65de7-e5aa-4197-85bd-219eab01e572@kernel.org>

>> test_bitmap: loaded.
>> test_bitmap: [lib/test_bitmap.c:397] bitmaps contents differ: expected 
>> "1,3-4,9", got "1,3-4,9,65-71,73-79,81-87,89-95,97-99"
>> test_bitmap: parselist('0-2047:128/256'):    912
>> test_bitmap: scnprintf("%*pbl", '0-32767'):    5977
>> test_bitmap: test_bitmap_read_perf:        1191082
>> test_bitmap: test_bitmap_write_perf:        1270153
>> test_bitmap: failed 1 out of 208655 tests
> 
> On QEMU (pmac32_defconfig) I get:
> 
> test_bitmap: loaded.
> test_bitmap: parselist('0-2047:128/256'):    2200
> test_bitmap: scnprintf("%*pbl", '0-32767'):    26440
> test_bitmap: test_bitmap_read_perf:        1819760
> test_bitmap: test_bitmap_write_perf:        18189840
> test_bitmap: all 208655 tests passed
> 
> On QEMU with your confirm I get:
> 
> [    1.162486] test_bitmap: loaded.
> [    1.177069] test_bitmap: [lib/test_bitmap.c:397] bitmaps contents 
> differ: expected "1,3-4,9", got "1,3-4,9,65-71,73-79,81-87,89-95,97-99"
> [    1.180599] test_bitmap: parselist('0-2047:128/256'):    2920
> [    1.190105] test_bitmap: scnprintf("%*pbl", '0-32767'):    65560
> [    1.427378] test_bitmap: test_bitmap_read_perf:        9546321
> [    1.438769] test_bitmap: test_bitmap_write_perf:        9344481
> [    1.457679] test_bitmap: failed 1 out of 208655 tests
> 
> So there is something with your config

Interesting! As I get the same test failure on my Talos II too.

Anyhow, I was able to bisect the issue. Offending commit is:

  # git bisect bad
6b5a4b68736798df1031404a2fad06d031253ef7 is the first bad commit
commit 6b5a4b68736798df1031404a2fad06d031253ef7 (HEAD)
Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Date:   Thu Feb 26 12:16:44 2026 +0100

     bitmap: Add test for out-of-boundary modifications for scatter & gather

     Make sure that bitmap_scatter() and bitmap_gather() do not modify
     the bits outside of the given nbits span.

     Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
     Signed-off-by: Yury Norov <ynorov@nvidia.com>

  lib/test_bitmap.c | 10 +++++++---
  1 file changed, 7 insertions(+), 3 deletions(-)


Reverting this commit on top of v7.2-rc2 lets the test pass:

test_bitmap: loaded.
test_bitmap: parselist('0-2047:128/256'):	888
test_bitmap: scnprintf("%*pbl", '0-32767'):	6074
test_bitmap: test_bitmap_read_perf:		1190938
test_bitmap: test_bitmap_write_perf:		1259471
test_bitmap: all 208655 tests passed

Your hint about my config made me check a few options and I found the 
offending one, which is INIT_STACK_ALL_PATTERN=y. On a kernel built with 
INIT_STACK_ALL_ZERO=y the issue does not show up.

Regards,
Erhard


^ permalink raw reply

* Re: [PATCH v8] mm: pgtable: free kernel page tables via RCU to fix ptdump UAF
From: David CARLIER @ 2026-07-07 17:08 UTC (permalink / raw)
  To: Lorenzo Stoakes
  Cc: Andrew Morton, Dev Jain, David Hildenbrand, Liam R . Howlett,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Dave Hansen, Lu Baolu, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Christophe Leroy, Ritesh Harjani,
	syzbot+fd95a72470f5a44e464c, linux-mm, linux-kernel, linux-riscv,
	linuxppc-dev
In-Reply-To: <ak0YI9KjSstnj9rJ@lucifer>

Sure thing :)

On Tue, 7 Jul 2026 at 16:19, Lorenzo Stoakes <ljs@kernel.org> wrote:
>
> Hi David C,
>
> This has been through a lot of revisions so I think for the sake of expediency
> the best solution here is for me to grab this and fiddle with it a bit then post
> it out myself.
>
> TO BE VERY CLEAR - I will keep your authorship and attribution :), and simply
> stick my name on a Co-developed-by tag.
>
> So this will remain your patch.
>
> Cheers, Lorenzo
>
> On Mon, Jul 06, 2026 at 09:31:27PM +0100, David Carlier wrote:
> > ptdump_walk_pgd() walks the kernel page tables under get_online_mems().
> > That does not stop vmalloc from freeing a kernel PTE page underneath the
> > walk.
> >
> > When vmap_try_huge_pmd() promotes a range to a huge PMD it collapses the
> > existing PTE table and frees it via pmd_free_pte_page(). On x86, riscv and
> > powerpc this runs without the init_mm mmap lock; only arm64 takes it, and
> > not on the block-split path. So ptdump can dereference a just-freed PTE
> > page, which is the use after free syzbot hit in ptdump_pte_entry().
> >
> > The race is not new. ptdump walks the whole kernel address space, including
> > ranges other code is actively mapping, so it reads page tables it does not
> > own. Commit 5ba2f0a15564 ("mm: introduce deferred freeing for kernel page
> > tables") only widened the window; the Fixes tag points there for that
> > reason.
> >
> > Every other walker works on a range it owns and is the only one mutating
> > it: set_memory() on arm64/riscv/loongarch, the arm64 block-split path, the
> > openrisc DMA path and the hugetlb_vmemmap remap. Nothing frees those ranges
> > concurrently, so they cannot race and do not need RCU. ptdump is the only
> > walker that traverses ranges it does not own.
> >
> > Defer the free by an RCU grace period. pagetable_free_kernel() now frees
> > via call_rcu() in both the async and non-async configs. The async path
> > still flushes the TLB first, then queues the per-page RCU free. The page
> > stays valid until any walk that may have observed it drops its RCU read
> > lock.
> >
> > x86, arm64 and riscv reach pagetable_free_kernel() for the collapsed PTE
> > page (the ptdesc carries PT_kernel), so the deferral covers them. powerpc
> > uses its own fragment allocator: pte_free_kernel() there frees the page
> > synchronously via pte_fragment_free() and the ptdesc is not flagged kernel,
> > so defer the kernel case in pte_fragment_free() as well. arm64 also takes
> > init_mm.mmap_lock around the free under a static key; that is now redundant
> > with the RCU deferral but left in place.
> >
> > On the read side walk_page_range_debug() walks the init_mm range in bounded
> > chunks, taking rcu_read_lock() around each chunk and calling cond_resched()
> > between them. It uses the lockless walker as the mmap lock is no longer
> > held, and pgd_addr_end() to bound each chunk without overflowing at the top
> > of the address space. A walker either sees the cleared PMD and skips, or
> > keeps the page alive until it drops the lock. The owned-range walkers are
> > unchanged.
> >
> > Stop taking mmap_write_lock() in ptdump_walk_pgd() for init_mm. It never
> > guarded against this free -- most architectures free the collapsed PTE
> > table without it -- and RCU now provides the synchronisation. efi_mm and
> > current->mm page tables are not RCU-freed, so they keep the mmap write
> > lock.
> >
> > ptdump callbacks run under RCU within a chunk, so they must not sleep. The
> > arch note_page() and effective_prot() callbacks only format into the
> > preallocated seq_file buffer; the only GFP_KERNEL marker setup runs before
> > the walk, and cond_resched() happens between chunks, outside the read lock.
> >
> > Fixes: 5ba2f0a15564 ("mm: introduce deferred freeing for kernel page tables")
> > Reported-by: syzbot+fd95a72470f5a44e464c@syzkaller.appspotmail.com
> > Closes: https://lore.kernel.org/all/6a287988.39669fcc.33b062.00a0.GAE@google.com/T/
> > Assisted-by: Claude:claude-opus-4-8
> > Signed-off-by: David Carlier <devnexen@gmail.com>
> > ---
> > v8: fix four issues raised by the Gemini review of v7 (relayed by Andrew).
> >     - the init_mm walk called walk_kernel_page_table_range(), which
> >       asserts init_mm.mmap_lock -- but that lock is now dropped; use the
> >       lockless walker instead.
> >     - only stop taking mmap_write_lock() for init_mm; efi_mm and
> >       current->mm are not RCU-freed and keep it.
> >     - bound each chunk with pgd_addr_end() so the last chunk does not
> >       overflow at the top of the address space.
> >     - powerpc frees kernel PTE pages synchronously via pte_fragment_free()
> >       and never sets PT_kernel, so it bypassed the RCU deferral; defer the
> >       kernel case there too.
> > v7: no code change; add version tag and per-revision changelog (Dev).
> > v6: chunk the init_mm walk in walk_page_range_debug() and take
> >     rcu_read_lock() per chunk (reverting v5's caller-side lock + assert)
> >     so the read section stays bounded on large kernel address spaces and
> >     can cond_resched() between chunks; drop the now-redundant
> >     mmap_write_lock() in ptdump_walk_pgd().
> > v5: reframe changelog around the pre-existing race and range ownership;
> >     correct the mmap-lock description (arm64 is the exception, not x86);
> >     move rcu_read_lock() into ptdump_walk_pgd() and assert it in
> >     walk_page_range_debug(); drop walk_kernel_page_table_range_rcu(); fix
> >     the pgtable-generic.c comment; document the no-sleep audit of the
> >     callbacks.
> > v4: defer the free in both the async and non-async configs, not just the
> >     async one; add a walk_kernel_page_table_range_rcu() helper.
> > v3: take rcu_read_lock() in the init_mm branch of walk_page_range_debug().
> > v2: use call_rcu() instead of synchronize_rcu().
> >  arch/powerpc/mm/pgtable-frag.c |  7 ++++++-
> >  include/linux/mm.h             |  7 -------
> >  mm/pagewalk.c                  | 36 +++++++++++++++++++++++++++++-----
> >  mm/pgtable-generic.c           | 22 ++++++++++++++++++++-
> >  mm/ptdump.c                    | 11 +++++++++--
> >  5 files changed, 67 insertions(+), 16 deletions(-)
> >
> > diff --git a/arch/powerpc/mm/pgtable-frag.c b/arch/powerpc/mm/pgtable-frag.c
> > index ae742564a3d5..1e1e88f831f7 100644
> > --- a/arch/powerpc/mm/pgtable-frag.c
> > +++ b/arch/powerpc/mm/pgtable-frag.c
> > @@ -123,7 +123,12 @@ void pte_fragment_free(unsigned long *table, int kernel)
> >
> >       BUG_ON(atomic_read(&ptdesc->pt_frag_refcount) <= 0);
> >       if (atomic_dec_and_test(&ptdesc->pt_frag_refcount)) {
> > -             if (kernel || !folio_test_clear_active(ptdesc_folio(ptdesc)))
> > +             /*
> > +              * Kernel page tables may be walked locklessly under RCU by
> > +              * ptdump, so defer their free by a grace period too, like the
> > +              * lockless-GUP case below for user tables.
> > +              */
> > +             if (!kernel && !folio_test_clear_active(ptdesc_folio(ptdesc)))
> >                       pte_free_now(&ptdesc->pt_rcu_head);
> >               else
> >                       call_rcu(&ptdesc->pt_rcu_head, pte_free_now);
> > diff --git a/include/linux/mm.h b/include/linux/mm.h
> > index 485df9c2dbdd..79408a17a1b0 100644
> > --- a/include/linux/mm.h
> > +++ b/include/linux/mm.h
> > @@ -3695,14 +3695,7 @@ static inline void __pagetable_free(struct ptdesc *pt)
> >       __free_pages(page, compound_order(page));
> >  }
> >
> > -#ifdef CONFIG_ASYNC_KERNEL_PGTABLE_FREE
> >  void pagetable_free_kernel(struct ptdesc *pt);
> > -#else
> > -static inline void pagetable_free_kernel(struct ptdesc *pt)
> > -{
> > -     __pagetable_free(pt);
> > -}
> > -#endif
> >  /**
> >   * pagetable_free - Free pagetables
> >   * @pt:      The page table descriptor
> > diff --git a/mm/pagewalk.c b/mm/pagewalk.c
> > index 3ae2586ff45b..fc2fe014ac8c 100644
> > --- a/mm/pagewalk.c
> > +++ b/mm/pagewalk.c
> > @@ -620,7 +620,7 @@ int walk_page_range(struct mm_struct *mm, unsigned long start,
> >   * Note: Be careful to walk the kernel pages tables, the caller may be need to
> >   * take other effective approaches (mmap lock may be insufficient) to prevent
> >   * the intermediate kernel page tables belonging to the specified address range
> > - * from being freed (e.g. memory hot-remove).
> > + * from being freed (e.g. memory hot-remove, vmap huge page promotion).
> >   */
> >  int walk_kernel_page_table_range(unsigned long start, unsigned long end,
> >               const struct mm_walk_ops *ops, pgd_t *pgd, void *private)
> > @@ -643,7 +643,7 @@ int walk_kernel_page_table_range(unsigned long start, unsigned long end,
> >   * Use this function to walk the kernel page tables locklessly. It should be
> >   * guaranteed that the caller has exclusive access over the range they are
> >   * operating on - that there should be no concurrent access, for example,
> > - * changing permissions for vmalloc objects.
> > + * changing permissions for vmalloc objects, or vmap huge page promotion.
> >   */
> >  int walk_kernel_page_table_range_lockless(unsigned long start, unsigned long end,
> >               const struct mm_walk_ops *ops, pgd_t *pgd, void *private)
> > @@ -692,9 +692,35 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start,
> >       };
> >
> >       /* For convenience, we allow traversal of kernel mappings. */
> > -     if (mm == &init_mm)
> > -             return walk_kernel_page_table_range(start, end, ops,
> > -                                                 pgd, private);
> > +     if (mm == &init_mm) {
> > +             unsigned long addr = start;
> > +
> > +             /*
> > +              * Walk in bounded chunks so the RCU read lock is never held
> > +              * across the whole kernel address space.  A kernel page table
> > +              * freed via pagetable_free_kernel() stays valid until the walk
> > +              * that may have observed it drops the lock; releasing the lock
> > +              * between chunks is safe as no page table pointer is held
> > +              * across the gap. The mmap lock is not held, so use the
> > +              * lockless walker; RCU, not the lock, keeps the table alive.
> > +              */
> > +             while (addr < end) {
> > +                     unsigned long next = pgd_addr_end(addr, end);
> > +                     int err;
> > +
> > +                     rcu_read_lock();
> > +                     err = walk_kernel_page_table_range_lockless(addr, next, ops,
> > +                                                                 pgd, private);
> > +                     rcu_read_unlock();
> > +                     if (err)
> > +                             return err;
> > +
> > +                     addr = next;
> > +                     cond_resched();
> > +             }
> > +             return 0;
> > +     }
> > +
> >       if (start >= end || !walk.mm)
> >               return -EINVAL;
> >       if (!check_ops_safe(ops))
> > diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c
> > index b91b1a98029c..7a32e4821957 100644
> > --- a/mm/pgtable-generic.c
> > +++ b/mm/pgtable-generic.c
> > @@ -410,6 +410,13 @@ pte_t *pte_offset_map_lock(struct mm_struct *mm, pmd_t *pmd,
> >       goto again;
> >  }
> >
> > +static void kernel_pgtable_free_rcu(struct rcu_head *head)
> > +{
> > +     struct ptdesc *pt = container_of(head, struct ptdesc, pt_rcu_head);
> > +
> > +     __pagetable_free(pt);
> > +}
> > +
> >  #ifdef CONFIG_ASYNC_KERNEL_PGTABLE_FREE
> >  static void kernel_pgtable_work_func(struct work_struct *work);
> >
> > @@ -434,8 +441,15 @@ static void kernel_pgtable_work_func(struct work_struct *work)
> >       spin_unlock(&kernel_pgtable_work.lock);
> >
> >       iommu_sva_invalidate_kva_range(PAGE_OFFSET, TLB_FLUSH_ALL);
> > +
> > +     /*
> > +      * Debug walkers (ptdump) may walk ranges they do not own and race this
> > +      * free, so they walk under rcu_read_lock(). Free after a grace period:
> > +      * a walker either already saw the cleared PMD, or keeps the page alive
> > +      * until it drops the RCU lock.
> > +      */
> >       list_for_each_entry_safe(pt, next, &page_list, pt_list)
> > -             __pagetable_free(pt);
> > +             call_rcu(&pt->pt_rcu_head, kernel_pgtable_free_rcu);
> >  }
> >
> >  void pagetable_free_kernel(struct ptdesc *pt)
> > @@ -446,4 +460,10 @@ void pagetable_free_kernel(struct ptdesc *pt)
> >
> >       schedule_work(&kernel_pgtable_work.work);
> >  }
> > +#else
> > +void pagetable_free_kernel(struct ptdesc *pt)
> > +{
> > +     /* Defer the free by a grace period; see kernel_pgtable_work_func(). */
> > +     call_rcu(&pt->pt_rcu_head, kernel_pgtable_free_rcu);
> > +}
> >  #endif
> > diff --git a/mm/ptdump.c b/mm/ptdump.c
> > index 973020000096..537be9995e1a 100644
> > --- a/mm/ptdump.c
> > +++ b/mm/ptdump.c
> > @@ -177,13 +177,20 @@ void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm, pgd_t *pgd)
> >       const struct ptdump_range *range = st->range;
> >
> >       get_online_mems();
> > -     mmap_write_lock(mm);
> > +     /*
> > +      * init_mm is walked locklessly under RCU by walk_page_range_debug().
> > +      * efi_mm / current->mm page tables are not RCU-freed, so hold the
> > +      * mmap write lock to keep them stable against concurrent teardown.
> > +      */
> > +     if (mm != &init_mm)
> > +             mmap_write_lock(mm);
> >       while (range->start != range->end) {
> >               walk_page_range_debug(mm, range->start, range->end,
> >                                     &ptdump_ops, pgd, st);
> >               range++;
> >       }
> > -     mmap_write_unlock(mm);
> > +     if (mm != &init_mm)
> > +             mmap_write_unlock(mm);
> >       put_online_mems();
> >
> >       /* Flush out the last page */
> > --
> > 2.53.0
> >


^ permalink raw reply

* Re: test_bitmap fails on ppc/ppc64 on kernels v7.1.3, v7.2-rc2
From: Christophe Leroy (CS GROUP) @ 2026-07-07 22:29 UTC (permalink / raw)
  To: Erhard Furtner, linuxppc-dev@lists.ozlabs.org; +Cc: linux-kernel
In-Reply-To: <ca3547ae-8b79-43a2-a758-23ec980bfd9a@mailbox.org>

Hi,

Le 06/07/2026 à 21:27, Erhard Furtner a écrit :
> Greetings!
> 
> I don't get this on my x86_64 machines, but on ppc32 (PowerMac G4 DP) 
> and on ppc64 (Talos II, POWER9) running Gentoo Linux. This dmesg snippet 
> is from the G4:
> 
> Total memory = 2048MB; using 4096kB for hash table
> Activating Kernel Userspace Access Protection
> Activating Kernel Userspace Execution Prevention
> Linux version 7.2.0-rc2-PMacG4 (root@T1000) (gcc (Gentoo 15.3.0 p8) 
> 15.3.0, GNU ld (Gentoo 2.46.0 p1) 2.46.0) #1 SMP PREEMPT Mon Jul  6 
> 20:22:47 CEST 2026
> 
> [...]
> 
> test_bitmap: loaded.
> test_bitmap: [lib/test_bitmap.c:397] bitmaps contents differ: expected 
> "1,3-4,9", got "1,3-4,9,65-71,73-79,81-87,89-95,97-99"
> test_bitmap: parselist('0-2047:128/256'):    912
> test_bitmap: scnprintf("%*pbl", '0-32767'):    5977
> test_bitmap: test_bitmap_read_perf:        1191082
> test_bitmap: test_bitmap_write_perf:        1270153
> test_bitmap: failed 1 out of 208655 tests

On QEMU (pmac32_defconfig) I get:

test_bitmap: loaded.
test_bitmap: parselist('0-2047:128/256'):	2200
test_bitmap: scnprintf("%*pbl", '0-32767'):	26440
test_bitmap: test_bitmap_read_perf:		1819760
test_bitmap: test_bitmap_write_perf:		18189840
test_bitmap: all 208655 tests passed

On QEMU with your confirm I get:

[    1.162486] test_bitmap: loaded.
[    1.177069] test_bitmap: [lib/test_bitmap.c:397] bitmaps contents 
differ: expected "1,3-4,9", got "1,3-4,9,65-71,73-79,81-87,89-95,97-99"
[    1.180599] test_bitmap: parselist('0-2047:128/256'):	2920
[    1.190105] test_bitmap: scnprintf("%*pbl", '0-32767'):	65560
[    1.427378] test_bitmap: test_bitmap_read_perf:		9546321
[    1.438769] test_bitmap: test_bitmap_write_perf:		9344481
[    1.457679] test_bitmap: failed 1 out of 208655 tests

So there is something with your config

> 
> 
> Full dmesg available at request. Kernel .config attached.
> 
> Regards,
> Erhard



^ permalink raw reply

* [patch 18/18] entry, treewide: Make syscall_enter_from_user_mode[_work]() indicate syscall execution
From: Thomas Gleixner @ 2026-07-07 19:07 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Michal Suchánek, Jonathan Corbet,
	Arnd Bergmann, Mark Rutland, Huacai Chen, Michael Ellerman,
	Shrikanth Hegde, Paul Walmsley, Palmer Dabbelt, Sven Schnelle,
	linux-doc, loongarch, linuxppc-dev, linux-riscv, linux-s390,
	Kees Cook, x86, Jinjie Ruan, Andy Lutomirski, Oleg Nesterov,
	Richard Henderson, Russell King, Catalin Marinas, Guo Ren,
	Geert Uytterhoeven, Thomas Bogendoerfer, Helge Deller,
	Yoshinori Sato, Richard Weinberger, Chris Zankel,
	linux-arm-kernel, linux-alpha, linux-csky, linux-m68k, linux-mips,
	linux-parisc, linux-sh, linux-um, Vineet Gupta, Will Deacon,
	Brian Cain, Michal Simek, Dinh Nguyen, David S. Miller,
	Andreas Larsson, linux-snps-arc, linux-hexagon, linux-openrisc,
	sparclinux, linux-arch
In-Reply-To: <20260707181957.433213175@kernel.org>

From: Michal Suchánek <msuchanek@suse.de>

The return values of syscall_enter_from_user_mode[_work]() are
non-intuitive. Both functions return the syscall number which should be
invoked by the architecture specific syscall entry code. The returned
number can be:

  - the unmodified syscall number which was handed in by the caller

  - a modified syscall number (ptrace, seccomp, trace/probe/bpf)

That has an additional twist. If the return value is -1L then the caller is
not allowed to modify the return value as that indicates that the modifying
entity requests to abort the syscall and set the return value already. That
can obviously not be differentiated from a syscall which handed in -1 as
syscall number.

The established way to deal with that is:

    set_return_value(regs, -ENOSYS);
    nr = syscall_enter_from_user_mode(regs, nr);
    if ((unsigned)nr < SYSCALLNR_MAX)
    	handle_syscall(regs, nr);
    else if (nr != -1)
    	set_return_value(regs, -ENOSYS);

The latter is obviously redundant, but that's just a leftover of the
historical evolution of this code. S390 has some special requirements here,
which can be avoided when the return value is not ambiguous.

Now that the functions which modify the syscall number and want to abort
are converted to indicate that with a boolean return value, it's obvious to
hand this through to the callers.

Rework syscall_enter_from_user_mode[_work]) so they take a pointer to the
syscall number and return a boolean, which indicates whether the syscall
should be handled or not.

That's not only more intuitive, it also results in slightly denser
executable code on x86 at least, but perf results are neutral and within
the noise.

[ tglx: Adopted it to the changes in the generic entry code, fixed up the
  	32-bit fallout and rewrote change log ]

Signed-off-by: Michal Suchánek <msuchanek@suse.de>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Huacai Chen <chenhuacai@kernel.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Shrikanth Hegde <sshegde@linux.ibm.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Sven Schnelle <svens@linux.ibm.com>
Cc: linux-doc@vger.kernel.org
Cc: loongarch@lists.linux.dev
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-riscv@lists.infradead.org
Cc: linux-s390@vger.kernel.org
---
 Documentation/core-api/entry.rst |   18 +++++++++++-------
 arch/loongarch/kernel/syscall.c  |   14 +++++++-------
 arch/powerpc/kernel/syscall.c    |    3 ++-
 arch/riscv/kernel/traps.c        |   11 +++++------
 arch/s390/kernel/syscall.c       |    7 +++++--
 arch/x86/entry/syscall_32.c      |   25 ++++++++++++-------------
 arch/x86/entry/syscall_64.c      |   12 ++++++------
 include/linux/entry-common.h     |   12 +++++-------
 8 files changed, 53 insertions(+), 49 deletions(-)

--- a/Documentation/core-api/entry.rst
+++ b/Documentation/core-api/entry.rst
@@ -68,16 +68,20 @@ low-level C code must not be instrumente
   noinstr void syscall(struct pt_regs *regs, int nr)
   {
 	arch_syscall_enter(regs);
-	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
-
-	instrumentation_begin();
-	if (!invoke_syscall(regs, nr) && nr != -1)
-	 	result_reg(regs) = __sys_ni_syscall(regs);
-	instrumentation_end();
-
+	result_reg(regs) = -ENOSYS;
+	if (syscall_enter_from_user_mode_randomize_stack(regs, &nr)) {
+		instrumentation_begin();
+		if (!invoke_syscall(regs, nr))
+			result_reg(regs) = __sys_ni_syscall(regs);
+		instrumentation_end();
+	}
 	syscall_exit_to_user_mode(regs);
   }
 
+It is required that either the low level assembly code or the syscall
+function sets the result register to -ENOSYS before invoking the generic
+code.
+
 syscall_enter_from_user_mode_randomize_stack() first invokes
 enter_from_user_mode_randomize_stack() which establishes state in the
 following order:
--- a/arch/loongarch/kernel/syscall.c
+++ b/arch/loongarch/kernel/syscall.c
@@ -57,8 +57,8 @@ typedef long (*sys_call_fn)(unsigned lon
 
 void noinstr __no_stack_protector do_syscall(struct pt_regs *regs)
 {
-	unsigned long nr;
 	sys_call_fn syscall_fn;
+	unsigned long nr;
 
 	nr = regs->regs[11];
 	/* Set for syscall restarting */
@@ -69,12 +69,12 @@ void noinstr __no_stack_protector do_sys
 	regs->orig_a0 = regs->regs[4];
 	regs->regs[4] = -ENOSYS;
 
-	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
-
-	if (nr < NR_syscalls) {
-		syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)];
-		regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6],
-					   regs->regs[7], regs->regs[8], regs->regs[9]);
+	if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
+		if (nr < NR_syscalls) {
+			syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)];
+			regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6],
+						   regs->regs[7], regs->regs[8], regs->regs[9]);
+		}
 	}
 
 	syscall_exit_to_user_mode(regs);
--- a/arch/powerpc/kernel/syscall.c
+++ b/arch/powerpc/kernel/syscall.c
@@ -18,7 +18,8 @@ notrace long system_call_exception(struc
 	long ret;
 	syscall_fn f;
 
-	r0 = syscall_enter_from_user_mode_randomize_stack(regs, r0);
+	if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0))
+		return syscall_get_error(current, regs);
 
 	if (unlikely(r0 >= NR_syscalls)) {
 		if (unlikely(trap_is_unsupported_scv(regs))) {
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -332,13 +332,12 @@ void do_trap_ecall_u(struct pt_regs *reg
 
 		riscv_v_vstate_discard(regs);
 
-		syscall = syscall_enter_from_user_mode_randomize_stack(regs, syscall);
-
-		if (syscall >= 0 && syscall < NR_syscalls) {
-			syscall = array_index_nospec(syscall, NR_syscalls);
-			syscall_handler(regs, syscall);
+		if (syscall_enter_from_user_mode_randomize_stack(regs, &syscall)) {
+			if (syscall >= 0 && syscall < NR_syscalls) {
+				syscall = array_index_nospec(syscall, NR_syscalls);
+				syscall_handler(regs, syscall);
+			}
 		}
-
 		syscall_exit_to_user_mode(regs);
 	} else {
 		irqentry_state_t state = irqentry_nmi_enter(regs);
--- a/arch/s390/kernel/syscall.c
+++ b/arch/s390/kernel/syscall.c
@@ -96,6 +96,7 @@ SYSCALL_DEFINE0(ni_syscall)
 void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
 {
 	unsigned long nr;
+	bool permit;
 
 	enter_from_user_mode_randomize_stack(regs);
 
@@ -121,7 +122,9 @@ void noinstr __do_syscall(struct pt_regs
 		regs->psw.addr = current->restart_block.arch_data;
 		current->restart_block.arch_data = 1;
 	}
-	nr = syscall_enter_from_user_mode_work(regs, nr);
+
+	permit = syscall_enter_from_user_mode_work(regs, &nr);
+
 	/*
 	 * In the s390 ptrace ABI, both the syscall number and the return value
 	 * use gpr2. However, userspace puts the syscall number either in the
@@ -129,7 +132,7 @@ void noinstr __do_syscall(struct pt_regs
 	 * work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
 	 * and if set, the syscall will be skipped.
 	 */
-	if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)))
+	if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET) || !permit))
 		goto out;
 	regs->gprs[2] = -ENOSYS;
 	if (likely(nr < NR_syscalls)) {
--- a/arch/x86/entry/syscall_32.c
+++ b/arch/x86/entry/syscall_32.c
@@ -161,8 +161,9 @@ static __always_inline bool int80_is_ext
 	nr = syscall_32_enter(regs);
 
 	local_irq_enable();
-	nr = syscall_enter_from_user_mode_work(regs, nr);
-	do_syscall_32_irqs_on(regs, nr);
+
+	if (likely(syscall_enter_from_user_mode_work(regs, &nr)))
+		do_syscall_32_irqs_on(regs, nr);
 
 	instrumentation_end();
 	syscall_exit_to_user_mode(regs);
@@ -223,8 +224,8 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
 	nr = syscall_32_enter(regs);
 
 	local_irq_enable();
-	nr = syscall_enter_from_user_mode_work(regs, nr);
-	do_syscall_32_irqs_on(regs, nr);
+	if (likely(syscall_enter_from_user_mode_work(regs, &nr)))
+		do_syscall_32_irqs_on(regs, nr);
 
 	instrumentation_end();
 	syscall_exit_to_user_mode(regs);
@@ -243,13 +244,13 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
 	 * orig_ax, the int return value truncates it. This matches
 	 * the semantics of syscall_get_nr().
 	 */
-	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
-
-	instrumentation_begin();
+	if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
+		instrumentation_begin();
 
-	do_syscall_32_irqs_on(regs, nr);
+		do_syscall_32_irqs_on(regs, nr);
 
-	instrumentation_end();
+		instrumentation_end();
+	}
 	syscall_exit_to_user_mode(regs);
 }
 #endif /* !CONFIG_IA32_EMULATION */
@@ -286,10 +287,8 @@ static noinstr bool __do_fast_syscall_32
 		return false;
 	}
 
-	nr = syscall_enter_from_user_mode_work(regs, nr);
-
-	/* Now this is just like a normal syscall. */
-	do_syscall_32_irqs_on(regs, nr);
+	if (likely(syscall_enter_from_user_mode_work(regs, &nr)))
+		do_syscall_32_irqs_on(regs, nr);
 
 	instrumentation_end();
 	syscall_exit_to_user_mode(regs);
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -78,14 +78,14 @@ static __always_inline void do_syscall_x
 /* Returns true to return using SYSRET, or false to use IRET */
 __visible noinstr bool do_syscall_64(struct pt_regs *regs, long nr)
 {
-	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
+	if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
+		instrumentation_begin();
 
-	instrumentation_begin();
+		if (!do_syscall_x64(regs, nr))
+			do_syscall_x32(regs, nr);
 
-	if (!do_syscall_x64(regs, nr))
-		do_syscall_x32(regs, nr);
-
-	instrumentation_end();
+		instrumentation_end();
+	}
 	syscall_exit_to_user_mode(regs);
 
 	/*
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -71,7 +71,7 @@ static inline void syscall_enter_audit(s
 	}
 }
 
-static __always_inline bool syscall_trace_enter(struct pt_regs *regs, unsigned long work,
+static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work,
 						long *syscall)
 {
 	/*
@@ -141,16 +141,14 @@ static __always_inline bool syscall_trac
  *     ptrace_report_syscall_permit_entry(), __seccomp_permit_syscall(), trace_sys_enter()
  *  2) Invocation of audit_syscall_entry()
  */
-static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall)
+static __always_inline bool syscall_enter_from_user_mode_work(struct pt_regs *regs, long *syscall)
 {
 	unsigned long work = READ_ONCE(current_thread_info()->syscall_work);
 
-	if (work & SYSCALL_WORK_ENTER) {
-		if (!syscall_trace_enter(regs, work, &syscall))
-			return -1L;
-	}
+	if (unlikely(work & SYSCALL_WORK_ENTER))
+		return syscall_trace_enter(regs, work, syscall);
 
-	return syscall;
+	return true;
 }
 
 /**



^ permalink raw reply

* [patch 17/18] x86/entry: Simplify the syscall number logic
From: Thomas Gleixner @ 2026-07-07 19:07 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Michael Ellerman, Shrikanth Hegde, linuxppc-dev,
	Kees Cook, Huacai Chen, loongarch, Paul Walmsley, Palmer Dabbelt,
	linux-riscv, Sven Schnelle, linux-s390, x86, Mark Rutland,
	Jinjie Ruan, Andy Lutomirski, Oleg Nesterov, Richard Henderson,
	Russell King, Catalin Marinas, Guo Ren, Geert Uytterhoeven,
	Thomas Bogendoerfer, Helge Deller, Yoshinori Sato,
	Richard Weinberger, Chris Zankel, linux-arm-kernel, linux-alpha,
	linux-csky, linux-m68k, linux-mips, linux-parisc, linux-sh,
	linux-um, Arnd Bergmann, Vineet Gupta, Will Deacon, Brian Cain,
	Michal Simek, Dinh Nguyen, David S. Miller, Andreas Larsson,
	linux-snps-arc, linux-hexagon, linux-openrisc, sparclinux,
	linux-arch, Michal Suchánek, Jonathan Corbet, linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

Converting from int to long, back to int and then to unsigned int is
confusing at best.

None of this voodoo is required. Negative syscall numbers including -1
don't need any of this treatment and the low level ASM code already does
the sign extension to 64-bit on a 64-bit kernel.

The only point where signedness matters is the comparison against the
maximum syscall number, but that can be simplified by just using a unsigned
argument for the various syscall invocation functions.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
---
 arch/x86/entry/syscall_32.c    |   26 +++++++++++---------------
 arch/x86/entry/syscall_64.c    |   36 ++++++++++++++----------------------
 arch/x86/include/asm/syscall.h |    2 +-
 3 files changed, 26 insertions(+), 38 deletions(-)

--- a/arch/x86/entry/syscall_32.c
+++ b/arch/x86/entry/syscall_32.c
@@ -41,6 +41,8 @@ const sys_call_ptr_t sys_call_table[] =
 #endif
 
 #define __SYSCALL(nr, sym) case nr: return __ia32_##sym(regs);
+
+/* The unsigned int @nr argument is intentional as it creates denser code in a 64-bit build */
 static noinline long ia32_sys_call(const struct pt_regs *regs, unsigned int nr)
 {
 	switch (nr) {
@@ -49,7 +51,7 @@ static noinline long ia32_sys_call(const
 	}
 }
 
-static __always_inline int syscall_32_enter(struct pt_regs *regs)
+static __always_inline long syscall_32_enter(struct pt_regs *regs)
 {
 	if (IS_ENABLED(CONFIG_IA32_EMULATION))
 		current_thread_info()->status |= TS_COMPAT;
@@ -70,17 +72,11 @@ early_param("ia32_emulation", ia32_emula
 /*
  * Invoke a 32-bit syscall.  Called with IRQs on in CT_STATE_KERNEL.
  */
-static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, int nr)
+static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, unsigned long nr)
 {
-	/*
-	 * Convert negative numbers to very high and thus out of range
-	 * numbers for comparisons.
-	 */
-	unsigned int unr = nr;
-
-	if (likely(unr < IA32_NR_syscalls)) {
-		unr = array_index_nospec(unr, IA32_NR_syscalls);
-		regs->ax = ia32_sys_call(regs, unr);
+	if (likely(nr < IA32_NR_syscalls)) {
+		nr = array_index_nospec(nr, IA32_NR_syscalls);
+		regs->ax = ia32_sys_call(regs, (unsigned int)nr);
 	}
 }
 
@@ -126,7 +122,7 @@ static __always_inline bool int80_is_ext
  */
 __visible noinstr void do_int80_emulation(struct pt_regs *regs)
 {
-	int nr;
+	long nr;
 
 	/* Kernel does not use INT $0x80! */
 	if (unlikely(!user_mode(regs))) {
@@ -205,7 +201,7 @@ static __always_inline bool int80_is_ext
  */
 DEFINE_FREDENTRY_RAW(int80_emulation)
 {
-	int nr;
+	long nr;
 
 	enter_from_user_mode_randomize_stack(regs);
 
@@ -240,7 +236,7 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
 /* Handles int $0x80 on a 32bit kernel */
 __visible noinstr void do_int80_syscall_32(struct pt_regs *regs)
 {
-	int nr = syscall_32_enter(regs);
+	long nr = syscall_32_enter(regs);
 
 	/*
 	 * Subtlety here: if ptrace pokes something larger than 2^31-1 into
@@ -260,7 +256,7 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
 
 static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)
 {
-	int nr = syscall_32_enter(regs);
+	long nr = syscall_32_enter(regs);
 	int res;
 
 	enter_from_user_mode_randomize_stack(regs);
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -32,6 +32,8 @@ const sys_call_ptr_t sys_call_table[] =
 #undef  __SYSCALL
 
 #define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs);
+
+/* The unsigned int @nr argument is intentional as it creates denser code */
 static noinline long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
 {
 	switch (nr) {
@@ -52,39 +54,29 @@ static noinline long x32_sys_call(const
 #endif
 }
 
-static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr)
+static __always_inline bool do_syscall_x64(struct pt_regs *regs, unsigned long nr)
 {
-	/*
-	 * Convert negative numbers to very high and thus out of range
-	 * numbers for comparisons.
-	 */
-	unsigned int unr = nr;
-
-	if (likely(unr < NR_syscalls)) {
-		unr = array_index_nospec(unr, NR_syscalls);
-		regs->ax = x64_sys_call(regs, unr);
+	if (likely(nr < NR_syscalls)) {
+		nr = array_index_nospec(nr, NR_syscalls);
+		regs->ax = x64_sys_call(regs, (unsigned int)nr);
 		return true;
 	}
 	return false;
 }
 
-static __always_inline void do_syscall_x32(struct pt_regs *regs, int nr)
+static __always_inline void do_syscall_x32(struct pt_regs *regs, unsigned long nr)
 {
-	/*
-	 * Adjust the starting offset of the table, and convert numbers
-	 * < __X32_SYSCALL_BIT to very high and thus out of range
-	 * numbers for comparisons.
-	 */
-	unsigned int xnr = nr - __X32_SYSCALL_BIT;
-
-	if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
-		xnr = array_index_nospec(xnr, X32_NR_syscalls);
-		regs->ax = x32_sys_call(regs, xnr);
+	/* Adjust the starting offset of the table */
+	nr -= __X32_SYSCALL_BIT;
+
+	if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(nr < X32_NR_syscalls)) {
+		nr = array_index_nospec(nr, X32_NR_syscalls);
+		regs->ax = x32_sys_call(regs, (unsigned int)nr);
 	}
 }
 
 /* Returns true to return using SYSRET, or false to use IRET */
-__visible noinstr bool do_syscall_64(struct pt_regs *regs, int nr)
+__visible noinstr bool do_syscall_64(struct pt_regs *regs, long nr)
 {
 	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
 
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -164,7 +164,7 @@ static inline int syscall_get_arch(struc
 		? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64;
 }
 
-bool do_syscall_64(struct pt_regs *regs, int nr);
+bool do_syscall_64(struct pt_regs *regs, long nr);
 void do_int80_emulation(struct pt_regs *regs);
 
 #endif	/* CONFIG_X86_32 */



^ permalink raw reply

* [patch 16/18] x86/entry: Get rid of the sys_ni_syscall() indirection
From: Thomas Gleixner @ 2026-07-07 19:07 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Michael Ellerman, Shrikanth Hegde, linuxppc-dev,
	Kees Cook, Huacai Chen, loongarch, Paul Walmsley, Palmer Dabbelt,
	linux-riscv, Sven Schnelle, linux-s390, x86, Mark Rutland,
	Jinjie Ruan, Andy Lutomirski, Oleg Nesterov, Richard Henderson,
	Russell King, Catalin Marinas, Guo Ren, Geert Uytterhoeven,
	Thomas Bogendoerfer, Helge Deller, Yoshinori Sato,
	Richard Weinberger, Chris Zankel, linux-arm-kernel, linux-alpha,
	linux-csky, linux-m68k, linux-mips, linux-parisc, linux-sh,
	linux-um, Arnd Bergmann, Vineet Gupta, Will Deacon, Brian Cain,
	Michal Simek, Dinh Nguyen, David S. Miller, Andreas Larsson,
	linux-snps-arc, linux-hexagon, linux-openrisc, sparclinux,
	linux-arch, Michal Suchánek, Jonathan Corbet, linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

Invoking sys_ni_syscall() from a code path, which already knows that the
syscall number is invalid just to assign -ENOSYS to regs->ax is a pointless
exercise. It's even redundant as the low level entry code already has set
regs->ax to -ENOSYS on entry.

Remove the extra conditionals and the function calls.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
---
 arch/x86/entry/syscall_32.c |    2 --
 arch/x86/entry/syscall_64.c |   10 +++-------
 2 files changed, 3 insertions(+), 9 deletions(-)

--- a/arch/x86/entry/syscall_32.c
+++ b/arch/x86/entry/syscall_32.c
@@ -81,8 +81,6 @@ static __always_inline void do_syscall_3
 	if (likely(unr < IA32_NR_syscalls)) {
 		unr = array_index_nospec(unr, IA32_NR_syscalls);
 		regs->ax = ia32_sys_call(regs, unr);
-	} else if (nr != -1) {
-		regs->ax = __ia32_sys_ni_syscall(regs);
 	}
 }
 
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -68,7 +68,7 @@ static __always_inline bool do_syscall_x
 	return false;
 }
 
-static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
+static __always_inline void do_syscall_x32(struct pt_regs *regs, int nr)
 {
 	/*
 	 * Adjust the starting offset of the table, and convert numbers
@@ -80,9 +80,7 @@ static __always_inline bool do_syscall_x
 	if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
 		xnr = array_index_nospec(xnr, X32_NR_syscalls);
 		regs->ax = x32_sys_call(regs, xnr);
-		return true;
 	}
-	return false;
 }
 
 /* Returns true to return using SYSRET, or false to use IRET */
@@ -92,10 +90,8 @@ static __always_inline bool do_syscall_x
 
 	instrumentation_begin();
 
-	if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) {
-		/* Invalid system call, but still a system call. */
-		regs->ax = __x64_sys_ni_syscall(regs);
-	}
+	if (!do_syscall_x64(regs, nr))
+		do_syscall_x32(regs, nr);
 
 	instrumentation_end();
 	syscall_exit_to_user_mode(regs);



^ permalink raw reply

* [patch 15/18] x86/entry: Make syscall functions static
From: Thomas Gleixner @ 2026-07-07 19:06 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Michael Ellerman, Shrikanth Hegde, linuxppc-dev,
	Kees Cook, Huacai Chen, loongarch, Paul Walmsley, Palmer Dabbelt,
	linux-riscv, Sven Schnelle, linux-s390, x86, Mark Rutland,
	Jinjie Ruan, Andy Lutomirski, Oleg Nesterov, Richard Henderson,
	Russell King, Catalin Marinas, Guo Ren, Geert Uytterhoeven,
	Thomas Bogendoerfer, Helge Deller, Yoshinori Sato,
	Richard Weinberger, Chris Zankel, linux-arm-kernel, linux-alpha,
	linux-csky, linux-m68k, linux-mips, linux-parisc, linux-sh,
	linux-um, Arnd Bergmann, Vineet Gupta, Will Deacon, Brian Cain,
	Michal Simek, Dinh Nguyen, David S. Miller, Andreas Larsson,
	linux-snps-arc, linux-hexagon, linux-openrisc, sparclinux,
	linux-arch, Michal Suchánek, Jonathan Corbet, linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

They are only used in the respective source files. No point in exposing
them.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
---
 arch/x86/entry/syscall_32.c    |    2 +-
 arch/x86/entry/syscall_64.c    |   10 ++++++----
 arch/x86/include/asm/syscall.h |    8 --------
 3 files changed, 7 insertions(+), 13 deletions(-)

--- a/arch/x86/entry/syscall_32.c
+++ b/arch/x86/entry/syscall_32.c
@@ -41,7 +41,7 @@ const sys_call_ptr_t sys_call_table[] =
 #endif
 
 #define __SYSCALL(nr, sym) case nr: return __ia32_##sym(regs);
-long ia32_sys_call(const struct pt_regs *regs, unsigned int nr)
+static noinline long ia32_sys_call(const struct pt_regs *regs, unsigned int nr)
 {
 	switch (nr) {
 	#include <asm/syscalls_32.h>
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -32,7 +32,7 @@ const sys_call_ptr_t sys_call_table[] =
 #undef  __SYSCALL
 
 #define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs);
-long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
+static noinline long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
 {
 	switch (nr) {
 	#include <asm/syscalls_64.h>
@@ -40,15 +40,17 @@ long x64_sys_call(const struct pt_regs *
 	}
 }
 
-#ifdef CONFIG_X86_X32_ABI
-long x32_sys_call(const struct pt_regs *regs, unsigned int nr)
+static noinline long x32_sys_call(const struct pt_regs *regs, unsigned int nr)
 {
+#ifdef CONFIG_X86_X32_ABI
 	switch (nr) {
 	#include <asm/syscalls_x32.h>
 	default: return __x64_sys_ni_syscall(regs);
 	}
-}
+#else
+	return -ENOSYS;
 #endif
+}
 
 static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr)
 {
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -21,14 +21,6 @@ typedef long (*sys_call_ptr_t)(const str
 extern const sys_call_ptr_t sys_call_table[];
 
 /*
- * These may not exist, but still put the prototypes in so we
- * can use IS_ENABLED().
- */
-extern long ia32_sys_call(const struct pt_regs *, unsigned int nr);
-extern long x32_sys_call(const struct pt_regs *, unsigned int nr);
-extern long x64_sys_call(const struct pt_regs *, unsigned int nr);
-
-/*
  * Only the low 32 bits of orig_ax are meaningful, so we return int.
  * This importantly ignores the high bits on 64-bit, so comparisons
  * sign-extend the low 32 bits.



^ permalink raw reply

* [patch 14/18] entry: Make return type of syscall_trace_enter() bool
From: Thomas Gleixner @ 2026-07-07 19:06 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Michael Ellerman, Shrikanth Hegde, linuxppc-dev,
	Kees Cook, Huacai Chen, loongarch, Paul Walmsley, Palmer Dabbelt,
	linux-riscv, Sven Schnelle, linux-s390, x86, Mark Rutland,
	Jinjie Ruan, Andy Lutomirski, Oleg Nesterov, Richard Henderson,
	Russell King, Catalin Marinas, Guo Ren, Geert Uytterhoeven,
	Thomas Bogendoerfer, Helge Deller, Yoshinori Sato,
	Richard Weinberger, Chris Zankel, linux-arm-kernel, linux-alpha,
	linux-csky, linux-m68k, linux-mips, linux-parisc, linux-sh,
	linux-um, Arnd Bergmann, Vineet Gupta, Will Deacon, Brian Cain,
	Michal Simek, Dinh Nguyen, David S. Miller, Andreas Larsson,
	linux-snps-arc, linux-hexagon, linux-openrisc, sparclinux,
	linux-arch, Michal Suchánek, Jonathan Corbet, linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

This prepares for changing the return types of
syscall_enter_from_user_mode[_work]() to bool, which in turn separates the
decision of invoking the syscall from the syscall number, which might have
been changed in the call by ptrace, seccomp, tracing.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
---
 include/linux/entry-common.h |   28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)

--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -71,8 +71,8 @@ static inline void syscall_enter_audit(s
 	}
 }
 
-static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work,
-						long syscall)
+static __always_inline bool syscall_trace_enter(struct pt_regs *regs, unsigned long work,
+						long *syscall)
 {
 	/*
 	 * Handle Syscall User Dispatch.  This must comes first, since
@@ -81,7 +81,7 @@ static __always_inline long syscall_trac
 	 */
 	if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
 		if (syscall_user_dispatch(regs))
-			return -1L;
+			return false;
 	}
 
 	/*
@@ -90,32 +90,32 @@ static __always_inline long syscall_trac
 	 * through hrtimer_interrupt().
 	 */
 	if (work & SYSCALL_WORK_SYSCALL_RSEQ_SLICE)
-		rseq_syscall_enter_work(syscall);
+		rseq_syscall_enter_work(*syscall);
 
 	/* Handle ptrace */
 	if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
 		if (!arch_ptrace_report_syscall_permit_entry(regs) ||
 		    (work & SYSCALL_WORK_SYSCALL_EMU))
-			return -1L;
+			return false;
 	}
 
 	/* Do seccomp after ptrace, to catch any tracer changes. */
 	if (work & SYSCALL_WORK_SECCOMP) {
 		if (!__seccomp_permit_syscall())
-			return -1L;
+			return false;
 	}
 
 	/* Either of the above might have changed the syscall number */
-	syscall = syscall_get_nr(current, regs);
+	*syscall = syscall_get_nr(current, regs);
 
 	if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT)) {
-		if (!trace_syscall_enter(regs, &syscall))
-			return -1L;
+		if (!trace_syscall_enter(regs, syscall))
+			return false;
 	}
 
-	syscall_enter_audit(regs, syscall);
+	syscall_enter_audit(regs, *syscall);
 
-	return syscall;
+	return true;
 }
 
 /**
@@ -145,8 +145,10 @@ static __always_inline long syscall_ente
 {
 	unsigned long work = READ_ONCE(current_thread_info()->syscall_work);
 
-	if (work & SYSCALL_WORK_ENTER)
-		syscall = syscall_trace_enter(regs, work, syscall);
+	if (work & SYSCALL_WORK_ENTER) {
+		if (!syscall_trace_enter(regs, work, &syscall))
+			return -1L;
+	}
 
 	return syscall;
 }



^ permalink raw reply

* [patch 13/18] entry: Make trace_syscall_enter() return type bool
From: Thomas Gleixner @ 2026-07-07 19:06 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Michael Ellerman, Shrikanth Hegde, linuxppc-dev,
	Kees Cook, Huacai Chen, loongarch, Paul Walmsley, Palmer Dabbelt,
	linux-riscv, Sven Schnelle, linux-s390, x86, Mark Rutland,
	Jinjie Ruan, Andy Lutomirski, Oleg Nesterov, Richard Henderson,
	Russell King, Catalin Marinas, Guo Ren, Geert Uytterhoeven,
	Thomas Bogendoerfer, Helge Deller, Yoshinori Sato,
	Richard Weinberger, Chris Zankel, linux-arm-kernel, linux-alpha,
	linux-csky, linux-m68k, linux-mips, linux-parisc, linux-sh,
	linux-um, Arnd Bergmann, Vineet Gupta, Will Deacon, Brian Cain,
	Michal Simek, Dinh Nguyen, David S. Miller, Andreas Larsson,
	linux-snps-arc, linux-hexagon, linux-openrisc, sparclinux,
	linux-arch, Michal Suchánek, Jonathan Corbet, linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

In preparation of converting the return value of
syscall_enter_from_user_mode[_work]() bool, rework trace_syscall_enter() to

 - update the syscall number via a pointer argument

 - Return True if the syscall number is != -1, False otherwise

That aligns with ptrace_report_syscall_permit_enter() and
seccomp_permit_syscall().

The only difference is that this also returns False, when the syscall
number was already -1 to begin with, but there is not much which can be
done about that. As the architecture has to preset the return value to
-ENOSYS anyway, that results in the correct return value for such an
invalid syscall.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
---
 include/linux/entry-common.h  |    8 +++++---
 kernel/entry/syscall-common.c |    7 ++++---
 2 files changed, 9 insertions(+), 6 deletions(-)

--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -58,7 +58,7 @@ static __always_inline bool arch_ptrace_
 #endif
 
 bool syscall_user_dispatch(struct pt_regs *regs);
-long trace_syscall_enter(struct pt_regs *regs, long syscall);
+bool trace_syscall_enter(struct pt_regs *regs, long *syscall);
 void trace_syscall_exit(struct pt_regs *regs, long ret);
 
 static inline void syscall_enter_audit(struct pt_regs *regs, long syscall)
@@ -108,8 +108,10 @@ static __always_inline long syscall_trac
 	/* Either of the above might have changed the syscall number */
 	syscall = syscall_get_nr(current, regs);
 
-	if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT))
-		syscall = trace_syscall_enter(regs, syscall);
+	if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT)) {
+		if (!trace_syscall_enter(regs, &syscall))
+			return -1L;
+	}
 
 	syscall_enter_audit(regs, syscall);
 
--- a/kernel/entry/syscall-common.c
+++ b/kernel/entry/syscall-common.c
@@ -7,14 +7,15 @@
 
 /* Out of line to prevent tracepoint code duplication */
 
-long trace_syscall_enter(struct pt_regs *regs, long syscall)
+bool trace_syscall_enter(struct pt_regs *regs, long *syscall)
 {
-	trace_sys_enter(regs, syscall);
+	trace_sys_enter(regs, *syscall);
 	/*
 	 * Probes or BPF hooks in the tracepoint may have changed the
 	 * system call number. Reread it.
 	 */
-	return syscall_get_nr(current, regs);
+	*syscall = syscall_get_nr(current, regs);
+	return *syscall != -1L;
 }
 
 void trace_syscall_exit(struct pt_regs *regs, long ret)



^ permalink raw reply

* [patch 12/18] ptrace, treewide: Rename ptrace_report_syscall_entry() to ptrace_report_syscall_permit_entry()
From: Thomas Gleixner @ 2026-07-07 19:06 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Arnd Bergmann, Oleg Nesterov, Richard Henderson,
	Vineet Gupta, Russell King, Catalin Marinas, Will Deacon, Guo Ren,
	Brian Cain, Geert Uytterhoeven, Michal Simek, Thomas Bogendoerfer,
	Dinh Nguyen, Helge Deller, Yoshinori Sato, David S. Miller,
	Andreas Larsson, Chris Zankel, linux-alpha, linux-snps-arc,
	linux-arm-kernel, linux-csky, linux-hexagon, linux-m68k,
	linux-mips, linux-openrisc, linux-parisc, linux-sh, sparclinux,
	linux-um, linux-arch, Michael Ellerman, Shrikanth Hegde,
	linuxppc-dev, Kees Cook, Huacai Chen, loongarch, Paul Walmsley,
	Palmer Dabbelt, linux-riscv, Sven Schnelle, linux-s390, x86,
	Mark Rutland, Jinjie Ruan, Andy Lutomirski, Richard Weinberger,
	Michal Suchánek, Jonathan Corbet, linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

The return value of that function is boolean and tells the caller whether
to permit the syscall processing or not.

Rename the function so the purpose is clear and make the return type bool.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Richard Henderson <richard.henderson@linaro.org>
Cc: Vineet Gupta <vgupta@kernel.org>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Guo Ren <guoren@kernel.org>
Cc: Brian Cain <bcain@kernel.org>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Michal Simek <monstr@monstr.eu>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Dinh Nguyen <dinguyen@kernel.org>
Cc: Helge Deller <deller@gmx.de>
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Andreas Larsson <andreas@gaisler.com>
Cc: Chris Zankel <chris@zankel.net>
Cc: linux-alpha@vger.kernel.org
Cc: linux-snps-arc@lists.infradead.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-csky@vger.kernel.org
Cc: linux-hexagon@vger.kernel.org
Cc: linux-m68k@lists.linux-m68k.org
Cc: linux-mips@vger.kernel.org
Cc: linux-openrisc@vger.kernel.org
Cc: linux-parisc@vger.kernel.org
Cc: linux-sh@vger.kernel.org
Cc: sparclinux@vger.kernel.org
Cc: linux-um@lists.infradead.org
Cc: linux-arch@vger.kernel.org
---
 arch/alpha/kernel/ptrace.c      |    2 +-
 arch/arc/kernel/ptrace.c        |    2 +-
 arch/arm/kernel/ptrace.c        |    2 +-
 arch/arm64/kernel/ptrace.c      |    2 +-
 arch/csky/kernel/ptrace.c       |    2 +-
 arch/hexagon/kernel/traps.c     |    2 +-
 arch/m68k/kernel/ptrace.c       |    2 +-
 arch/microblaze/kernel/ptrace.c |    2 +-
 arch/mips/kernel/ptrace.c       |    2 +-
 arch/nios2/kernel/ptrace.c      |    2 +-
 arch/openrisc/kernel/ptrace.c   |    2 +-
 arch/parisc/kernel/ptrace.c     |   10 ++++------
 arch/sh/kernel/ptrace_32.c      |    2 +-
 arch/sparc/kernel/ptrace_32.c   |    2 +-
 arch/sparc/kernel/ptrace_64.c   |    2 +-
 arch/um/kernel/ptrace.c         |    2 +-
 arch/xtensa/kernel/ptrace.c     |    2 +-
 include/asm-generic/syscall.h   |    4 ++--
 include/linux/entry-common.h    |   25 ++++++++++++-------------
 include/linux/ptrace.h          |   13 ++++++-------
 20 files changed, 40 insertions(+), 44 deletions(-)

--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -375,7 +375,7 @@ asmlinkage unsigned long syscall_trace_e
 	struct pt_regs *regs = current_pt_regs();
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
-		ptrace_report_syscall_entry(regs)) {
+		!ptrace_report_syscall_permit_entry(regs)) {
 		syscall_set_nr(current, regs, -1);
 		if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
 			syscall_set_return_value(current, regs, -ENOSYS, 0);
--- a/arch/arc/kernel/ptrace.c
+++ b/arch/arc/kernel/ptrace.c
@@ -342,7 +342,7 @@ long arch_ptrace(struct task_struct *chi
 asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 {
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
-		if (ptrace_report_syscall_entry(regs))
+		if (!ptrace_report_syscall_permit_entry(regs))
 			return ULONG_MAX;
 
 #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -840,7 +840,7 @@ static void report_syscall(struct pt_reg
 
 	if (dir == PTRACE_SYSCALL_EXIT)
 		ptrace_report_syscall_exit(regs, 0);
-	else if (ptrace_report_syscall_entry(regs))
+	else if (!ptrace_report_syscall_permit_entry(regs))
 		current_thread_info()->abi_syscall = -1;
 
 	regs->ARM_ip = ip;
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2379,7 +2379,7 @@ static int report_syscall_entry(struct p
 	int regno, ret;
 
 	saved_reg = ptrace_save_reg(regs, PTRACE_SYSCALL_ENTER, &regno);
-	ret = ptrace_report_syscall_entry(regs);
+	ret = !ptrace_report_syscall_permit_entry(regs);
 	if (ret)
 		forget_syscall(regs);
 	regs->regs[regno] = saved_reg;
--- a/arch/csky/kernel/ptrace.c
+++ b/arch/csky/kernel/ptrace.c
@@ -320,7 +320,7 @@ long arch_ptrace(struct task_struct *chi
 asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 {
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
-		if (ptrace_report_syscall_entry(regs))
+		if (!ptrace_report_syscall_permit_entry(regs))
 			return -1;
 
 	if (!seccomp_permit_syscall())
--- a/arch/hexagon/kernel/traps.c
+++ b/arch/hexagon/kernel/traps.c
@@ -345,7 +345,7 @@ void do_trap0(struct pt_regs *regs)
 
 		/* allow strace to catch syscall args  */
 		if (unlikely(test_thread_flag(TIF_SYSCALL_TRACE) &&
-			ptrace_report_syscall_entry(regs)))
+			!ptrace_report_syscall_permit_entry(regs)))
 			return;  /*  return -ENOSYS somewhere?  */
 
 		/* Interrupts should be re-enabled for syscall processing */
--- a/arch/m68k/kernel/ptrace.c
+++ b/arch/m68k/kernel/ptrace.c
@@ -279,7 +279,7 @@ asmlinkage int syscall_trace_enter(void)
 	int ret = 0;
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
-		ret = ptrace_report_syscall_entry(task_pt_regs(current));
+		ret = !ptrace_report_syscall_permit_entry(task_pt_regs(current));
 
 	if (!seccomp_permit_syscall())
 		return -1;
--- a/arch/microblaze/kernel/ptrace.c
+++ b/arch/microblaze/kernel/ptrace.c
@@ -139,7 +139,7 @@ asmlinkage unsigned long do_syscall_trac
 	secure_computing_strict(regs->r12);
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
-	    ptrace_report_syscall_entry(regs))
+	    !ptrace_report_syscall_permit_entry(regs))
 		/*
 		 * Tracing decided this syscall should not happen.
 		 * We'll return a bogus call number to get an ENOSYS
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -1324,7 +1324,7 @@ asmlinkage long syscall_trace_enter(stru
 	user_exit();
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE)) {
-		if (ptrace_report_syscall_entry(regs))
+		if (!ptrace_report_syscall_permit_entry(regs))
 			return -1;
 	}
 
--- a/arch/nios2/kernel/ptrace.c
+++ b/arch/nios2/kernel/ptrace.c
@@ -133,7 +133,7 @@ asmlinkage int do_syscall_trace_enter(vo
 	int ret = 0;
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
-		ret = ptrace_report_syscall_entry(task_pt_regs(current));
+		ret = !ptrace_report_syscall_permit_entry(task_pt_regs(current));
 
 	return ret;
 }
--- a/arch/openrisc/kernel/ptrace.c
+++ b/arch/openrisc/kernel/ptrace.c
@@ -293,7 +293,7 @@ asmlinkage long do_syscall_trace_enter(s
 	long ret = 0;
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
-	    ptrace_report_syscall_entry(regs))
+	    !ptrace_report_syscall_permit_entry(regs))
 		/*
 		 * Tracing decided this syscall should not happen.
 		 * We'll return a bogus call number to get an ENOSYS
--- a/arch/parisc/kernel/ptrace.c
+++ b/arch/parisc/kernel/ptrace.c
@@ -326,7 +326,7 @@ long compat_arch_ptrace(struct task_stru
 long do_syscall_trace_enter(struct pt_regs *regs)
 {
 	if (test_thread_flag(TIF_SYSCALL_TRACE)) {
-		int rc = ptrace_report_syscall_entry(regs);
+		bool permit = ptrace_report_syscall_permit_entry(regs);
 
 		/*
 		 * As tracesys_next does not set %r28 to -ENOSYS
@@ -334,12 +334,10 @@ long do_syscall_trace_enter(struct pt_re
 		 */
 		regs->gr[28] = -ENOSYS;
 
-		if (rc) {
+		if (!permit) {
 			/*
-			 * A nonzero return code from
-			 * ptrace_report_syscall_entry() tells us
-			 * to prevent the syscall execution.  Skip
-			 * the syscall call and the syscall restart handling.
+			 * Skip the syscall call and the syscall restart
+			 * handling.
 			 *
 			 * Note that the tracer may also just change
 			 * regs->gr[20] to an invalid syscall number,
--- a/arch/sh/kernel/ptrace_32.c
+++ b/arch/sh/kernel/ptrace_32.c
@@ -455,7 +455,7 @@ long arch_ptrace(struct task_struct *chi
 asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 {
 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
-	    ptrace_report_syscall_entry(regs)) {
+	    !ptrace_report_syscall_permit_entry(regs)) {
 		regs->regs[0] = -ENOSYS;
 		return -1;
 	}
--- a/arch/sparc/kernel/ptrace_32.c
+++ b/arch/sparc/kernel/ptrace_32.c
@@ -441,7 +441,7 @@ asmlinkage int syscall_trace(struct pt_r
 		if (syscall_exit_p)
 			ptrace_report_syscall_exit(regs, 0);
 		else
-			ret = ptrace_report_syscall_entry(regs);
+			ret = !ptrace_report_syscall_permit_entry(regs);
 	}
 
 	return ret;
--- a/arch/sparc/kernel/ptrace_64.c
+++ b/arch/sparc/kernel/ptrace_64.c
@@ -1093,7 +1093,7 @@ asmlinkage int syscall_trace_enter(struc
 		user_exit();
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
-		ret = ptrace_report_syscall_entry(regs);
+		ret = !ptrace_report_syscall_permit_entry(regs);
 
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 		trace_sys_enter(regs, regs->u_regs[UREG_G1]);
--- a/arch/um/kernel/ptrace.c
+++ b/arch/um/kernel/ptrace.c
@@ -135,7 +135,7 @@ int syscall_trace_enter(struct pt_regs *
 	if (!test_thread_flag(TIF_SYSCALL_TRACE))
 		return 0;
 
-	return ptrace_report_syscall_entry(regs);
+	return !ptrace_report_syscall_permit_entry(regs);
 }
 
 void syscall_trace_leave(struct pt_regs *regs)
--- a/arch/xtensa/kernel/ptrace.c
+++ b/arch/xtensa/kernel/ptrace.c
@@ -547,7 +547,7 @@ int do_syscall_trace_enter(struct pt_reg
 		regs->areg[2] = -ENOSYS;
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
-	    ptrace_report_syscall_entry(regs)) {
+	    !ptrace_report_syscall_permit_entry(regs)) {
 		regs->areg[2] = -ENOSYS;
 		regs->syscall = NO_SYSCALL;
 		return 0;
--- a/include/asm-generic/syscall.h
+++ b/include/asm-generic/syscall.h
@@ -58,8 +58,8 @@ void syscall_set_nr(struct task_struct *
  *
  * It's only valid to call this when @task is stopped for system
  * call exit tracing (due to %SYSCALL_WORK_SYSCALL_TRACE or
- * %SYSCALL_WORK_SYSCALL_AUDIT), after ptrace_report_syscall_entry()
- * returned nonzero to prevent the system call from taking place.
+ * %SYSCALL_WORK_SYSCALL_AUDIT), after ptrace_report_syscall_permit_entry()
+ * returned False to prevent the system call from taking place.
  *
  * This rolls back the register state in @regs so it's as if the
  * system call instruction was a no-op.  The registers containing
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -38,21 +38,22 @@
 				 SYSCALL_WORK_SYSCALL_EXIT_TRAP)
 
 /**
- * arch_ptrace_report_syscall_entry - Architecture specific ptrace_report_syscall_entry() wrapper
+ * arch_ptrace_report_syscall_permit_entry - Architecture specific wrapper for
+ *					     ptrace_report_syscall_permit_entry()
  * @regs: Pointer to the register state at syscall entry
  *
- * Invoked from syscall_trace_enter() to wrap ptrace_report_syscall_entry().
+ * Invoked from syscall_trace_enter() to wrap ptrace_report_syscall_permit_entry().
  *
- * This allows architecture specific ptrace_report_syscall_entry()
+ * This allows architecture specific ptrace_report_syscall_permit_entry()
  * implementations. If not defined by the architecture this falls back to
- * to ptrace_report_syscall_entry().
+ * to ptrace_report_syscall_permit_entry().
  */
-static __always_inline int arch_ptrace_report_syscall_entry(struct pt_regs *regs);
+static __always_inline bool arch_ptrace_report_syscall_permit_entry(struct pt_regs *regs);
 
-#ifndef arch_ptrace_report_syscall_entry
-static __always_inline int arch_ptrace_report_syscall_entry(struct pt_regs *regs)
+#ifndef arch_ptrace_report_syscall_permit_entry
+static __always_inline bool arch_ptrace_report_syscall_permit_entry(struct pt_regs *regs)
 {
-	return ptrace_report_syscall_entry(regs);
+	return ptrace_report_syscall_permit_entry(regs);
 }
 #endif
 
@@ -73,8 +74,6 @@ static inline void syscall_enter_audit(s
 static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work,
 						long syscall)
 {
-	long ret = 0;
-
 	/*
 	 * Handle Syscall User Dispatch.  This must comes first, since
 	 * the ABI here can be something that doesn't make sense for
@@ -95,8 +94,8 @@ static __always_inline long syscall_trac
 
 	/* Handle ptrace */
 	if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
-		ret = arch_ptrace_report_syscall_entry(regs);
-		if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
+		if (!arch_ptrace_report_syscall_permit_entry(regs) ||
+		    (work & SYSCALL_WORK_SYSCALL_EMU))
 			return -1L;
 	}
 
@@ -137,7 +136,7 @@ static __always_inline long syscall_trac
  * It handles the following work items:
  *
  *  1) syscall_work flag dependent invocations of
- *     ptrace_report_syscall_entry(), __seccomp_permit_syscall(), trace_sys_enter()
+ *     ptrace_report_syscall_permit_entry(), __seccomp_permit_syscall(), trace_sys_enter()
  *  2) Invocation of audit_syscall_entry()
  */
 static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall)
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -405,13 +405,13 @@ extern void sigaction_compat_abi(struct
 /*
  * ptrace report for syscall entry and exit looks identical.
  */
-static inline int ptrace_report_syscall(unsigned long message)
+static inline bool ptrace_report_syscall(unsigned long message)
 {
 	int ptrace = current->ptrace;
 	int signr;
 
 	if (!(ptrace & PT_PTRACED))
-		return 0;
+		return true;
 
 	signr = ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0),
 			      message);
@@ -424,11 +424,11 @@ static inline int ptrace_report_syscall(
 	if (signr)
 		send_sig(signr, current, 1);
 
-	return fatal_signal_pending(current);
+	return !fatal_signal_pending(current);
 }
 
 /**
- * ptrace_report_syscall_entry - task is about to attempt a system call
+ * ptrace_report_syscall_permit_entry - task is about to attempt a system call
  * @regs:		user register state of current task
  *
  * This will be called if %SYSCALL_WORK_SYSCALL_TRACE or
@@ -438,7 +438,7 @@ static inline int ptrace_report_syscall(
  * call number and arguments to be tried.  It is safe to block here,
  * preventing the system call from beginning.
  *
- * Returns zero normally, or nonzero if the calling arch code should abort
+ * Returns True normally, or False if the calling architecture code should abort
  * the system call.  That must prevent normal entry so no system call is
  * made.  If @task ever returns to user mode after this, its register state
  * is unspecified, but should be something harmless like an %ENOSYS error
@@ -447,8 +447,7 @@ static inline int ptrace_report_syscall(
  *
  * Called without locks, just after entering kernel mode.
  */
-static inline __must_check int ptrace_report_syscall_entry(
-	struct pt_regs *regs)
+static inline __must_check bool ptrace_report_syscall_permit_entry(struct pt_regs *regs)
 {
 	return ptrace_report_syscall(PTRACE_EVENTMSG_SYSCALL_ENTRY);
 }



^ permalink raw reply

* [patch 11/18] seccomp, treewide: Rename and convert __secure_computing() to return boolean
From: Thomas Gleixner @ 2026-07-07 19:06 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Mark Rutland, Jinjie Ruan, Kees Cook,
	Andy Lutomirski, Oleg Nesterov, Richard Henderson, Russell King,
	Catalin Marinas, Guo Ren, Geert Uytterhoeven, Thomas Bogendoerfer,
	Helge Deller, Yoshinori Sato, Richard Weinberger, Chris Zankel,
	linux-arm-kernel, linux-alpha, linux-csky, linux-m68k, linux-mips,
	linux-parisc, linux-sh, linux-um, Michael Ellerman,
	Shrikanth Hegde, linuxppc-dev, Huacai Chen, loongarch,
	Paul Walmsley, Palmer Dabbelt, linux-riscv, Sven Schnelle,
	linux-s390, x86, Arnd Bergmann, Vineet Gupta, Will Deacon,
	Brian Cain, Michal Simek, Dinh Nguyen, David S. Miller,
	Andreas Larsson, linux-snps-arc, linux-hexagon, linux-openrisc,
	sparclinux, linux-arch, Michal Suchánek, Jonathan Corbet,
	linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

From: Jinjie Ruan <ruanjinjie@huawei.com>

The return value of __secure_computing() currently uses 0 to indicate
that a system call should be allowed, and -1 to indicate that it should
be blocked/killed. This 0/-1 pattern is non-intuitive for a security
check function and makes the control flow at the call sites less readable.

Furthermore, any potential future changes to these return values would
require a high-risk, error-prone audit of all its users across different
architectures.

Sanitize this logic by converting the return type of __secure_computing()
to a proper boolean, where 'true' explicitly means 'allow' and 'false'
means 'fail/deny'.

Update all the two dozen or so call sites across the tree to align with
this new boolean semantic. No functional changes are intended, as the
callers still return -1 to the lower-level assembly entry code upon
seccomp denial.

Rename the function to __seccomp_permit_syscall() so that the purpose is
entirely clear.

[ tglx: Rename the function ]

Suggested-by: Thomas Gleixner <tglx@kernel.org>
Suggested-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Cc: Kees Cook <kees@kernel.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Richard Henderson <richard.henderson@linaro.org>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Guo Ren <guoren@kernel.org>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Helge Deller <deller@gmx.de>
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Cc: Richard Weinberger <richard@nod.at>
Cc: Chris Zankel <chris@zankel.net>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-alpha@vger.kernel.org
Cc: linux-csky@vger.kernel.org
Cc: linux-m68k@lists.linux-m68k.org
Cc: linux-mips@vger.kernel.org
Cc: linux-parisc@vger.kernel.org
Cc: linux-sh@vger.kernel.org
Cc: linux-um@lists.infradead.org
---
 arch/alpha/kernel/ptrace.c            |    2 -
 arch/arm/kernel/ptrace.c              |    2 -
 arch/arm64/kernel/ptrace.c            |    2 -
 arch/csky/kernel/ptrace.c             |    2 -
 arch/m68k/kernel/ptrace.c             |    2 -
 arch/mips/kernel/ptrace.c             |    2 -
 arch/parisc/kernel/ptrace.c           |    2 -
 arch/sh/kernel/ptrace_32.c            |    2 -
 arch/um/kernel/skas/syscall.c         |    2 -
 arch/x86/entry/vsyscall/vsyscall_64.c |   14 ++++++-------
 arch/xtensa/kernel/ptrace.c           |    3 --
 include/linux/entry-common.h          |    9 +++-----
 include/linux/seccomp.h               |   12 +++++------
 kernel/seccomp.c                      |   35 +++++++++++++++++-----------------
 14 files changed, 45 insertions(+), 46 deletions(-)
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -387,7 +387,7 @@ asmlinkage unsigned long syscall_trace_e
 	 * If this fails, seccomp may already have set up the return value
 	 * (e.g. SECCOMP_RET_ERRNO / TRACE).
 	 */
-	if (secure_computing() == -1) {
+	if (!seccomp_permit_syscall()) {
 		if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
 			syscall_set_return_value(current, regs, -ENOSYS, 0);
 		syscall_set_nr(current, regs, -1);
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -855,7 +855,7 @@ asmlinkage int syscall_trace_enter(struc
 
 	/* Do seccomp after ptrace; syscall may have changed. */
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
-	if (secure_computing() == -1)
+	if (!seccomp_permit_syscall())
 		return -1;
 #else
 	/* XXX: remove this once OABI gets fixed */
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2420,7 +2420,7 @@ int syscall_trace_enter(struct pt_regs *
 	}
 
 	/* Do the secure computing after ptrace; failures should be fast. */
-	if (secure_computing() == -1)
+	if (!seccomp_permit_syscall())
 		return NO_SYSCALL;
 
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
--- a/arch/csky/kernel/ptrace.c
+++ b/arch/csky/kernel/ptrace.c
@@ -323,7 +323,7 @@ asmlinkage int syscall_trace_enter(struc
 		if (ptrace_report_syscall_entry(regs))
 			return -1;
 
-	if (secure_computing() == -1)
+	if (!seccomp_permit_syscall())
 		return -1;
 
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
--- a/arch/m68k/kernel/ptrace.c
+++ b/arch/m68k/kernel/ptrace.c
@@ -281,7 +281,7 @@ asmlinkage int syscall_trace_enter(void)
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
 		ret = ptrace_report_syscall_entry(task_pt_regs(current));
 
-	if (secure_computing() == -1)
+	if (!seccomp_permit_syscall())
 		return -1;
 
 	return ret;
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -1328,7 +1328,7 @@ asmlinkage long syscall_trace_enter(stru
 			return -1;
 	}
 
-	if (secure_computing())
+	if (!seccomp_permit_syscall())
 		return -1;
 
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
--- a/arch/parisc/kernel/ptrace.c
+++ b/arch/parisc/kernel/ptrace.c
@@ -351,7 +351,7 @@ long do_syscall_trace_enter(struct pt_re
 	}
 
 	/* Do the secure computing check after ptrace. */
-	if (secure_computing() == -1)
+	if (!seccomp_permit_syscall())
 		return -1;
 
 #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
--- a/arch/sh/kernel/ptrace_32.c
+++ b/arch/sh/kernel/ptrace_32.c
@@ -460,7 +460,7 @@ asmlinkage long do_syscall_trace_enter(s
 		return -1;
 	}
 
-	if (secure_computing() == -1)
+	if (!seccomp_permit_syscall())
 		return -1;
 
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
--- a/arch/um/kernel/skas/syscall.c
+++ b/arch/um/kernel/skas/syscall.c
@@ -27,7 +27,7 @@ void handle_syscall(struct uml_pt_regs *
 		goto out;
 
 	/* Do the seccomp check after ptrace; failures should be fast. */
-	if (secure_computing() == -1)
+	if (!seccomp_permit_syscall())
 		goto out;
 
 	syscall = UPT_SYSCALL_NR(r);
--- a/arch/x86/entry/vsyscall/vsyscall_64.c
+++ b/arch/x86/entry/vsyscall/vsyscall_64.c
@@ -118,10 +118,10 @@ static bool write_ok_or_segv(unsigned lo
 
 static bool __emulate_vsyscall(struct pt_regs *regs, unsigned long address)
 {
-	unsigned long caller;
-	int vsyscall_nr, syscall_nr, tmp;
+	unsigned long caller, orig_dx;
+	int vsyscall_nr, syscall_nr;
+	bool skip;
 	long ret;
-	unsigned long orig_dx;
 
 	/* Confirm that the fault happened in 64-bit user mode */
 	if (!user_64bit_mode(regs))
@@ -197,16 +197,16 @@ static bool __emulate_vsyscall(struct pt
 	 */
 	regs->orig_ax = syscall_nr;
 	regs->ax = -ENOSYS;
-	tmp = secure_computing();
-	if ((!tmp && regs->orig_ax != syscall_nr) || regs->ip != address) {
+	skip = !seccomp_permit_syscall();
+	if ((!skip && regs->orig_ax != syscall_nr) || regs->ip != address) {
 		warn_bad_vsyscall(KERN_DEBUG, regs,
 				  "seccomp tried to change syscall nr or ip");
 		force_exit_sig(SIGSYS);
 		return true;
 	}
 	regs->orig_ax = -1;
-	if (tmp)
-		goto do_ret;  /* skip requested */
+	if (skip)
+		goto do_ret;
 
 	/*
 	 * With a real vsyscall, page faults cause SIGSEGV.
--- a/arch/xtensa/kernel/ptrace.c
+++ b/arch/xtensa/kernel/ptrace.c
@@ -553,8 +553,7 @@ int do_syscall_trace_enter(struct pt_reg
 		return 0;
 	}
 
-	if (regs->syscall == NO_SYSCALL ||
-	    secure_computing() == -1) {
+	if (regs->syscall == NO_SYSCALL || !seccomp_permit_syscall()) {
 		do_syscall_trace_leave(regs);
 		return 0;
 	}
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -102,9 +102,8 @@ static __always_inline long syscall_trac
 
 	/* Do seccomp after ptrace, to catch any tracer changes. */
 	if (work & SYSCALL_WORK_SECCOMP) {
-		ret = __secure_computing();
-		if (ret == -1L)
-			return ret;
+		if (!__seccomp_permit_syscall())
+			return -1L;
 	}
 
 	/* Either of the above might have changed the syscall number */
@@ -115,7 +114,7 @@ static __always_inline long syscall_trac
 
 	syscall_enter_audit(regs, syscall);
 
-	return ret ? : syscall;
+	return syscall;
 }
 
 /**
@@ -138,7 +137,7 @@ static __always_inline long syscall_trac
  * It handles the following work items:
  *
  *  1) syscall_work flag dependent invocations of
- *     ptrace_report_syscall_entry(), __secure_computing(), trace_sys_enter()
+ *     ptrace_report_syscall_entry(), __seccomp_permit_syscall(), trace_sys_enter()
  *  2) Invocation of audit_syscall_entry()
  */
 static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall)
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -22,14 +22,14 @@
 #include <linux/atomic.h>
 #include <asm/seccomp.h>
 
-extern int __secure_computing(void);
+extern bool __seccomp_permit_syscall(void);
 
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
-static inline int secure_computing(void)
+static __always_inline bool seccomp_permit_syscall(void)
 {
 	if (unlikely(test_syscall_work(SECCOMP)))
-		return  __secure_computing();
-	return 0;
+		return  __seccomp_permit_syscall();
+	return true;
 }
 #else
 extern void secure_computing_strict(int this_syscall);
@@ -50,11 +50,11 @@ static inline int seccomp_mode(struct se
 struct seccomp_data;
 
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
-static inline int secure_computing(void) { return 0; }
+static inline bool seccomp_permit_syscall(void) { return true; }
 #else
 static inline void secure_computing_strict(int this_syscall) { return; }
 #endif
-static inline int __secure_computing(void) { return 0; }
+static inline bool __seccomp_permit_syscall(void) { return true; }
 
 static inline long prctl_get_seccomp(void)
 {
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -1100,12 +1100,13 @@ void secure_computing_strict(int this_sy
 	else
 		BUG();
 }
-int __secure_computing(void)
+
+bool __seccomp_permit_syscall(void)
 {
 	int this_syscall = syscall_get_nr(current, current_pt_regs());
 
 	secure_computing_strict(this_syscall);
-	return 0;
+	return true;
 }
 #else
 
@@ -1256,7 +1257,7 @@ static int seccomp_do_user_notification(
 	return -1;
 }
 
-static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
+static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace)
 {
 	u32 filter_ret, action;
 	struct seccomp_data sd;
@@ -1294,7 +1295,7 @@ static int __seccomp_filter(int this_sys
 	case SECCOMP_RET_TRACE:
 		/* We've been put in this state by the ptracer already. */
 		if (recheck_after_trace)
-			return 0;
+			return true;
 
 		/* ENOSYS these calls if there is no tracer attached. */
 		if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) {
@@ -1330,19 +1331,19 @@ static int __seccomp_filter(int this_sys
 		 * a skip would have already been reported.
 		 */
 		if (__seccomp_filter(this_syscall, true))
-			return -1;
+			return false;
 
-		return 0;
+		return true;
 
 	case SECCOMP_RET_USER_NOTIF:
 		if (seccomp_do_user_notification(this_syscall, match, &sd))
 			goto skip;
 
-		return 0;
+		return true;
 
 	case SECCOMP_RET_LOG:
 		seccomp_log(this_syscall, 0, action, true);
-		return 0;
+		return true;
 
 	case SECCOMP_RET_ALLOW:
 		/*
@@ -1350,7 +1351,7 @@ static int __seccomp_filter(int this_sys
 		 * this action since SECCOMP_RET_ALLOW is the starting
 		 * state in seccomp_run_filters().
 		 */
-		return 0;
+		return true;
 
 	case SECCOMP_RET_KILL_THREAD:
 	case SECCOMP_RET_KILL_PROCESS:
@@ -1367,46 +1368,46 @@ static int __seccomp_filter(int this_sys
 		} else {
 			do_exit(SIGSYS);
 		}
-		return -1; /* skip the syscall go directly to signal handling */
+		return false; /* skip the syscall go directly to signal handling */
 	}
 
 	unreachable();
 
 skip:
 	seccomp_log(this_syscall, 0, action, match ? match->log : false);
-	return -1;
+	return false;
 }
 #else
-static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
+static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace)
 {
 	BUG();
 
-	return -1;
+	return false;
 }
 #endif
 
-int __secure_computing(void)
+bool __seccomp_permit_syscall(void)
 {
 	int mode = current->seccomp.mode;
 	int this_syscall;
 
 	if (IS_ENABLED(CONFIG_CHECKPOINT_RESTORE) &&
 	    unlikely(current->ptrace & PT_SUSPEND_SECCOMP))
-		return 0;
+		return true;
 
 	this_syscall = syscall_get_nr(current, current_pt_regs());
 
 	switch (mode) {
 	case SECCOMP_MODE_STRICT:
 		__secure_computing_strict(this_syscall);  /* may call do_exit */
-		return 0;
+		return true;
 	case SECCOMP_MODE_FILTER:
 		return __seccomp_filter(this_syscall, false);
 	/* Surviving SECCOMP_RET_KILL_* must be proactively impossible. */
 	case SECCOMP_MODE_DEAD:
 		WARN_ON_ONCE(1);
 		do_exit(SIGKILL);
-		return -1;
+		return false;
 	default:
 		BUG();
 	}



^ permalink raw reply

* [patch 10/18] entry: Use syscall number instead of rereading it
From: Thomas Gleixner @ 2026-07-07 19:06 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Michael Ellerman, Shrikanth Hegde, linuxppc-dev,
	Kees Cook, Huacai Chen, loongarch, Paul Walmsley, Palmer Dabbelt,
	linux-riscv, Sven Schnelle, linux-s390, x86, Mark Rutland,
	Jinjie Ruan, Andy Lutomirski, Oleg Nesterov, Richard Henderson,
	Russell King, Catalin Marinas, Guo Ren, Geert Uytterhoeven,
	Thomas Bogendoerfer, Helge Deller, Yoshinori Sato,
	Richard Weinberger, Chris Zankel, linux-arm-kernel, linux-alpha,
	linux-csky, linux-m68k, linux-mips, linux-parisc, linux-sh,
	linux-um, Arnd Bergmann, Vineet Gupta, Will Deacon, Brian Cain,
	Michal Simek, Dinh Nguyen, David S. Miller, Andreas Larsson,
	linux-snps-arc, linux-hexagon, linux-openrisc, sparclinux,
	linux-arch, Michal Suchánek, Jonathan Corbet, linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

rseq_syscall_enter_work() is invoked before the syscall number can be
modified. So there is no point in rereading it from pt_regs.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
---
 include/linux/entry-common.h |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -70,9 +70,10 @@ static inline void syscall_enter_audit(s
 	}
 }
 
-static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work)
+static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work,
+						long syscall)
 {
-	long syscall, ret = 0;
+	long ret = 0;
 
 	/*
 	 * Handle Syscall User Dispatch.  This must comes first, since
@@ -90,7 +91,7 @@ static __always_inline long syscall_trac
 	 * through hrtimer_interrupt().
 	 */
 	if (work & SYSCALL_WORK_SYSCALL_RSEQ_SLICE)
-		rseq_syscall_enter_work(syscall_get_nr(current, regs));
+		rseq_syscall_enter_work(syscall);
 
 	/* Handle ptrace */
 	if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
@@ -145,7 +146,7 @@ static __always_inline long syscall_ente
 	unsigned long work = READ_ONCE(current_thread_info()->syscall_work);
 
 	if (work & SYSCALL_WORK_ENTER)
-		syscall = syscall_trace_enter(regs, work);
+		syscall = syscall_trace_enter(regs, work, syscall);
 
 	return syscall;
 }



^ permalink raw reply

* [patch 09/18] entry: Remove syscall_enter_from_user_mode()
From: Thomas Gleixner @ 2026-07-07 19:06 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, Michael Ellerman, Shrikanth Hegde, linuxppc-dev,
	Kees Cook, Huacai Chen, loongarch, Paul Walmsley, Palmer Dabbelt,
	linux-riscv, Sven Schnelle, linux-s390, x86, Mark Rutland,
	Jinjie Ruan, Andy Lutomirski, Oleg Nesterov, Richard Henderson,
	Russell King, Catalin Marinas, Guo Ren, Geert Uytterhoeven,
	Thomas Bogendoerfer, Helge Deller, Yoshinori Sato,
	Richard Weinberger, Chris Zankel, linux-arm-kernel, linux-alpha,
	linux-csky, linux-m68k, linux-mips, linux-parisc, linux-sh,
	linux-um, Arnd Bergmann, Vineet Gupta, Will Deacon, Brian Cain,
	Michal Simek, Dinh Nguyen, David S. Miller, Andreas Larsson,
	linux-snps-arc, linux-hexagon, linux-openrisc, sparclinux,
	linux-arch, Michal Suchánek, Jonathan Corbet, linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

All architecture use either:

    nr = enter_from_user_mode_randomize_stack(regs, nr);

or

    enter_from_user_mode_randomize_stack(regs);
    nr = syscall_enter_from_user_mode_work(regs, nr);

Remove the now unused function.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
---
 Documentation/core-api/entry.rst |   17 +++++++++-------
 include/linux/entry-common.h     |   40 +++------------------------------------
 include/linux/irq-entry-common.h |    6 ++---
 3 files changed, 17 insertions(+), 46 deletions(-)

--- a/Documentation/core-api/entry.rst
+++ b/Documentation/core-api/entry.rst
@@ -68,7 +68,7 @@ low-level C code must not be instrumente
   noinstr void syscall(struct pt_regs *regs, int nr)
   {
 	arch_syscall_enter(regs);
-	nr = syscall_enter_from_user_mode(regs, nr);
+	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
 
 	instrumentation_begin();
 	if (!invoke_syscall(regs, nr) && nr != -1)
@@ -78,12 +78,14 @@ low-level C code must not be instrumente
 	syscall_exit_to_user_mode(regs);
   }
 
-syscall_enter_from_user_mode() first invokes enter_from_user_mode() which
-establishes state in the following order:
+syscall_enter_from_user_mode_randomize_stack() first invokes
+enter_from_user_mode_randomize_stack() which establishes state in the
+following order:
 
   * Lockdep
   * RCU / Context tracking
   * Tracing
+  * Apply stack randomization
 
 and then invokes the various entry work functions like ptrace, seccomp, audit,
 syscall tracing, etc. After all that is done, the instrumentable invoke_syscall
@@ -99,10 +101,11 @@ that it invokes exit_to_user_mode() whic
   * RCU / Context tracking
   * Lockdep
 
-syscall_enter_from_user_mode() and syscall_exit_to_user_mode() are also
-available as fine grained subfunctions in cases where the architecture code
-has to do extra work between the various steps. In such cases it has to
-ensure that enter_from_user_mode() is called first on entry and
+syscall_enter_from_user_mode_randomize_stack() and
+syscall_exit_to_user_mode() are also available as fine grained subfunctions
+in cases where the architecture code has to do extra work between the
+various steps. In such cases it has to ensure that
+enter_from_user_mode_randomize_stack() is called first on entry and
 exit_to_user_mode() is called last on exit.
 
 Do not nest syscalls. Nested syscalls will cause RCU and/or context tracking
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -19,7 +19,7 @@
 #endif
 
 /*
- * SYSCALL_WORK flags handled in syscall_enter_from_user_mode()
+ * SYSCALL_WORK flags handled in syscall_enter_from_user_mode_work()
  */
 #define SYSCALL_WORK_ENTER	(SYSCALL_WORK_SECCOMP |			\
 				 SYSCALL_WORK_SYSCALL_TRACEPOINT |	\
@@ -205,42 +205,10 @@ do {									\
 	_ret;								\
 })
 
-/**
- * syscall_enter_from_user_mode - Establish state and check and handle work
- *				  before invoking a syscall
- * @regs:	Pointer to currents pt_regs
- * @syscall:	The syscall number
- *
- * Invoked from architecture specific syscall entry code with interrupts
- * disabled. The calling code has to be non-instrumentable. When the
- * function returns all state is correct, interrupts are enabled and the
- * subsequent functions can be instrumented.
- *
- * This is the combination of enter_from_user_mode() and
- * syscall_enter_from_user_mode_work() to be used when there is no
- * architecture specific work to be done between the two.
- *
- * Returns: The original or a modified syscall number. See
- * syscall_enter_from_user_mode_work() for further explanation.
- */
-static __always_inline long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall)
-{
-	long ret;
-
-	enter_from_user_mode(regs);
-
-	instrumentation_begin();
-	local_irq_enable();
-	ret = syscall_enter_from_user_mode_work(regs, syscall);
-	instrumentation_end();
-
-	return ret;
-}
-
 /*
- * If SYSCALL_EMU is set, then the only reason to report is when
- * SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP).  This syscall
- * instruction has been already reported in syscall_enter_from_user_mode().
+ * If SYSCALL_EMU is set, then the only reason to report is when SINGLESTEP is
+ * set (i.e. PTRACE_SYSEMU_SINGLESTEP).  This syscall instruction has been
+ * already reported in syscall_enter_from_user_mode_work().
  */
 static __always_inline bool report_single_step(unsigned long work)
 {
--- a/include/linux/irq-entry-common.h
+++ b/include/linux/irq-entry-common.h
@@ -49,9 +49,9 @@
  * Defaults to an empty implementation. Can be replaced by architecture
  * specific code.
  *
- * Invoked from syscall_enter_from_user_mode() in the non-instrumentable
- * section. Use __always_inline so the compiler cannot push it out of line
- * and make it instrumentable.
+ * Invoked from enter_from_user_mode_syscall_and_randomize_stack() in the
+ * non-instrumentable section. Use __always_inline so the compiler cannot push
+ * it out of line and make it instrumentable.
  */
 static __always_inline void arch_enter_from_user_mode(struct pt_regs *regs);
 



^ permalink raw reply

* [patch 08/18] x86/syscall: Use [syscall_]enter_from_user_mode_randomize_stack()
From: Thomas Gleixner @ 2026-07-07 19:06 UTC (permalink / raw)
  To: LKML
  Cc: Peter Zijlstra, x86, Michael Ellerman, Shrikanth Hegde,
	linuxppc-dev, Kees Cook, Huacai Chen, loongarch, Paul Walmsley,
	Palmer Dabbelt, linux-riscv, Sven Schnelle, linux-s390,
	Mark Rutland, Jinjie Ruan, Andy Lutomirski, Oleg Nesterov,
	Richard Henderson, Russell King, Catalin Marinas, Guo Ren,
	Geert Uytterhoeven, Thomas Bogendoerfer, Helge Deller,
	Yoshinori Sato, Richard Weinberger, Chris Zankel,
	linux-arm-kernel, linux-alpha, linux-csky, linux-m68k, linux-mips,
	linux-parisc, linux-sh, linux-um, Arnd Bergmann, Vineet Gupta,
	Will Deacon, Brian Cain, Michal Simek, Dinh Nguyen,
	David S. Miller, Andreas Larsson, linux-snps-arc, linux-hexagon,
	linux-openrisc, sparclinux, linux-arch, Michal Suchánek,
	Jonathan Corbet, linux-doc
In-Reply-To: <20260707181957.433213175@kernel.org>

These functions integrate the stack randomization.

syscall_enter_from_user_mode_randomize_stack() has the advantage that the
randomization happens early right after enter_from_user_mode().

In both cases also the overhead of get/put_cpu_var() in
add_random_kstack_offset() is avoided.

No functional change.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Cc: x86@kernel.org
---
 arch/x86/entry/syscall_32.c         |   19 +++++--------------
 arch/x86/entry/syscall_64.c         |    3 +--
 arch/x86/include/asm/entry-common.h |    1 -
 3 files changed, 6 insertions(+), 17 deletions(-)

--- a/arch/x86/entry/syscall_32.c
+++ b/arch/x86/entry/syscall_32.c
@@ -142,10 +142,9 @@ static __always_inline bool int80_is_ext
 	 * int80_is_external() below which calls into the APIC driver.
 	 * Identical for soft and external interrupts.
 	 */
-	enter_from_user_mode(regs);
+	enter_from_user_mode_randomize_stack(regs);
 
 	instrumentation_begin();
-	add_random_kstack_offset();
 
 	/* Validate that this is a soft interrupt to the extent possible */
 	if (unlikely(int80_is_external()))
@@ -210,11 +209,9 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
 {
 	int nr;
 
-	enter_from_user_mode(regs);
+	enter_from_user_mode_randomize_stack(regs);
 
 	instrumentation_begin();
-	add_random_kstack_offset();
-
 	/*
 	 * FRED pushed 0 into regs::orig_ax and regs::ax contains the
 	 * syscall number.
@@ -252,10 +249,10 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
 	 * orig_ax, the int return value truncates it. This matches
 	 * the semantics of syscall_get_nr().
 	 */
-	nr = syscall_enter_from_user_mode(regs, nr);
+	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
+
 	instrumentation_begin();
 
-	add_random_kstack_offset();
 	do_syscall_32_irqs_on(regs, nr);
 
 	instrumentation_end();
@@ -268,15 +265,9 @@ static noinstr bool __do_fast_syscall_32
 	int nr = syscall_32_enter(regs);
 	int res;
 
-	/*
-	 * This cannot use syscall_enter_from_user_mode() as it has to
-	 * fetch EBP before invoking any of the syscall entry work
-	 * functions.
-	 */
-	enter_from_user_mode(regs);
+	enter_from_user_mode_randomize_stack(regs);
 
 	instrumentation_begin();
-	add_random_kstack_offset();
 	local_irq_enable();
 	/* Fetch EBP from where the vDSO stashed it. */
 	if (IS_ENABLED(CONFIG_X86_64)) {
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -86,10 +86,9 @@ static __always_inline bool do_syscall_x
 /* Returns true to return using SYSRET, or false to use IRET */
 __visible noinstr bool do_syscall_64(struct pt_regs *regs, int nr)
 {
-	nr = syscall_enter_from_user_mode(regs, nr);
+	nr = syscall_enter_from_user_mode_randomize_stack(regs, nr);
 
 	instrumentation_begin();
-	add_random_kstack_offset();
 
 	if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) {
 		/* Invalid system call, but still a system call. */
--- a/arch/x86/include/asm/entry-common.h
+++ b/arch/x86/include/asm/entry-common.h
@@ -2,7 +2,6 @@
 #ifndef _ASM_X86_ENTRY_COMMON_H
 #define _ASM_X86_ENTRY_COMMON_H
 
-#include <linux/randomize_kstack.h>
 #include <linux/user-return-notifier.h>
 
 #include <asm/nospec-branch.h>



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox