mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/2] sparc: Add architecture support for clone3
@ 2026-01-19 14:47 Ludwig Rydberg
  2026-01-19 14:47 ` [PATCH v2 1/2] sparc: Synchronize user stack on fork and clone Ludwig Rydberg
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Ludwig Rydberg @ 2026-01-19 14:47 UTC (permalink / raw)
  To: davem, andreas, brauner, shuah
  Cc: sparclinux, linux-kselftest, linux-kernel, arnd, glaubitz, geert,
	schuster.simon, jrtc27

Hi all,

This series adds support for the clone3 system call to the SPARC{32|64}
architectures and also adds a related patch for clone/fork/vfork that fix an
issue previously reported[1] that could result in -EFAULT for no good reason.
Without this patch, the clone3 system call would need the same mitigation as
introduced in glibc[2] for the clone system call.

About "sparc: Synchronize user stack on fork and clone"
---------------------------------------------------------

The clone3 implementation is developed on top of a fix for an issue reported
by Adrian Glaubitz[1], where a clone call could return -EFAULT. This problem
has since been mitigated in glibc[2] by synchronizing the user stack before
calling clone.

The root cause analysis of the kernel side when running the program in [1]
shows that the window spill handler routine on both SPARC{32|64} is unable to
flush a user window to the stack (due to MMU related faults) when flushing all
windows before handling the syscall. This then results in a -EFAULT when
copy_thread() fails to clone the uncommitted stackframe of the parent.

For SPARC32:
Prior to calling the syscall wrappers for clone/fork/vork all windows are
flushed by a macro (FLUSH_ALL_KERNEL_WINDOWS).
In the window spill trap handler, MMU fault-handling is temporarily
disabled while storing the window. If the window can't be stored
(which normally would have triggered a fault trap) the routine
backups the user window and increments a thread counter (wsaved).

For SPARC64:
Prior to calling the syscall wrappers for clone/fork/vork all windows are
flushed by issuing the flushw instruction.
In the window spill trap handler, if an exception triggers, then the user
window is added to the thread's user window buffer (in kernel memory) and
a thread counter (wsaved) counter is incremented.

Both SPARC{32|64}:
Eventually copy_thread will be called, which then will fail to clone the parent
stackframe to the child as the user window has not been flushed to the stack.

Fixed by adding a call to synchronize_user_stack() prior to calling
kernel_clone(). The patch has been tested both with and without the mitigation
in glibc by running the program mentioned in [1].

SPARC32:
- Tested in QEMU emulating sun4m using Buildroot 2025.02
  (qemu_sparc_ss10_defconfig).
- Tested on LEON using a GR-CPCI-GR740 development board from
  Frontgrade Gaisler.

SPARC64:
- Tested in QEMU emulating sun4u using Buildroot 2025.02
  (qemu_sparc64_sun4u_defconfig).

About the clone3 implementation:
--------------------------------

The implementation in the architectural port follows the same pattern as for the
original clone syscall. But instead of explicitly calling kernel_clone (as in
sparc_clone) the clone3 handler calls the generic sys_clone3 handler
(in kernel/fork). To get this to work without a user provided stack,
the copy_thread functions had to be updated to handle cl_args.stack == NULL.
In this case the stack of the parent is re-used.

The return value convention for clone3 follows the regular kernel return
value convention (in contrast to the original clone/fork on SPARC).

In sparc_{clone|vfork|fork} there is a workaround due to the SunOS
return value convention which is not needed for clone3.

When applying the patch series on top of v6.19-rc1 the relevant clone3 tests of
kselftest pass:

# /usr/lib/kselftests/run_kselftest.sh -c clone3 -s
kselftest: Running tests in clone3
TAP version 13
1..4
# selftests: clone3: clone3
ok 1 selftests: clone3: clone3
# selftests: clone3: clone3_clear_sighand
ok 2 selftests: clone3: clone3_clear_sighand
# selftests: clone3: clone3_set_tid
ok 3 selftests: clone3: clone3_set_tid
# selftests: clone3: clone3_cap_checkpoint_restore
ok 4 selftests: clone3: clone3_cap_checkpoint_restore

Note that the clone3_cap_checkpoint test failed in the same way as mentioned in
[3] (due to incompatibility with the libcap version on my system).
When applying the patch from [4] or by downgrading libcap to 2.59 the test pass.

SPARC32:
- Tested in QEMU emulating sun4m using Buildroot 2025.02
  (qemu_sparc_ss10_defconfig).
- Tested on LEON using a GR-CPCI-GR740 development board from
  Frontgrade Gaisler.

SPARC64:
- Tested in QEMU emulating sun4u using Buildroot 2025.02
  (qemu_sparc64_sun4u_defconfig).
- Tested on UltraSparc T4

[1]: https://lore.kernel.org/sparclinux/3ae4130c-c5aa-428e-b819-44cf2daf2af1@mkarcher.dialup.fu-berlin.de/
[2]: https://sourceware.org/bugzilla/show_bug.cgi?id=31394
[3]: https://lore.kernel.org/all/20250901-nios2-implement-clone3-v2-0-53fcf5577d57@siemens-energy.com/
[4]: https://lore.kernel.org/all/20241105062948.1037011-1-zhouyuhang1010@163.com/

---
Changes in v2:
- Added Tested-by tag from Adrian Glaubitz
- Implement generic Linux return value convention for clone3
- Drop SPARC handling for sys_clone3 in selftests
- Remove legacy ABI return value handling in sparc_clone3
- Link to v1: https://lore.kernel.org/sparclinux/20260116153051.21678-1-ludwig.rydberg@gaisler.com/
---

Andreas Larsson (1):
  sparc: Synchronize user stack on fork and clone

Ludwig Rydberg (1):
  sparc: Add architecture support for clone3

 arch/sparc/include/asm/syscalls.h      |  1 +
 arch/sparc/include/asm/unistd.h        |  2 -
 arch/sparc/kernel/entry.S              | 15 ++++++++
 arch/sparc/kernel/kernel.h             |  1 +
 arch/sparc/kernel/process.c            | 52 +++++++++++++++++++-------
 arch/sparc/kernel/process_32.c         | 23 +++++++++---
 arch/sparc/kernel/process_64.c         | 27 ++++++++++---
 arch/sparc/kernel/syscalls.S           |  8 ++++
 arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
 9 files changed, 102 insertions(+), 29 deletions(-)


base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
-- 
2.35.3


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/2] sparc: Synchronize user stack on fork and clone
  2026-01-19 14:47 [PATCH v2 0/2] sparc: Add architecture support for clone3 Ludwig Rydberg
@ 2026-01-19 14:47 ` Ludwig Rydberg
  2026-01-19 14:47 ` [PATCH v2 2/2] sparc: Add architecture support for clone3 Ludwig Rydberg
  2026-01-26 17:03 ` [PATCH v2 0/2] " Andreas Larsson
  2 siblings, 0 replies; 7+ messages in thread
From: Ludwig Rydberg @ 2026-01-19 14:47 UTC (permalink / raw)
  To: davem, andreas, brauner, shuah
  Cc: sparclinux, linux-kselftest, linux-kernel, arnd, glaubitz, geert,
	schuster.simon, jrtc27, kernel

From: Andreas Larsson <andreas@gaisler.com>

Flush all uncommitted user windows before calling the generic syscall
handlers for clone, fork, and vfork.

Prior to entering the arch common handlers sparc_{clone|fork|vfork}, the
arch-specific syscall wrappers for these syscalls will attempt to flush
all windows (including user windows).

In the window overflow trap handlers on both SPARC{32|64},
if the window can't be stored (i.e due to MMU related faults) the routine
backups the user window and increments a thread counter (wsaved).

By adding a synchronization point after the flush attempt, when fault
handling is enabled, any uncommitted user windows will be flushed.

Link: https://sourceware.org/bugzilla/show_bug.cgi?id=31394
Closes: https://lore.kernel.org/sparclinux/fe5cc47167430007560501aabb28ba154985b661.camel@physik.fu-berlin.de/
Signed-off-by: Andreas Larsson <andreas@gaisler.com>
Tested-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com>
---
 arch/sparc/kernel/process.c | 38 +++++++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 14 deletions(-)

diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
index 0442ab00518d..7d69877511fa 100644
--- a/arch/sparc/kernel/process.c
+++ b/arch/sparc/kernel/process.c
@@ -17,14 +17,18 @@
 
 asmlinkage long sparc_fork(struct pt_regs *regs)
 {
-	unsigned long orig_i1 = regs->u_regs[UREG_I1];
+	unsigned long orig_i1;
 	long ret;
 	struct kernel_clone_args args = {
 		.exit_signal	= SIGCHLD,
-		/* Reuse the parent's stack for the child. */
-		.stack		= regs->u_regs[UREG_FP],
 	};
 
+	synchronize_user_stack();
+
+	orig_i1 = regs->u_regs[UREG_I1];
+	/* Reuse the parent's stack for the child. */
+	args.stack = regs->u_regs[UREG_FP];
+
 	ret = kernel_clone(&args);
 
 	/* If we get an error and potentially restart the system
@@ -40,16 +44,19 @@ asmlinkage long sparc_fork(struct pt_regs *regs)
 
 asmlinkage long sparc_vfork(struct pt_regs *regs)
 {
-	unsigned long orig_i1 = regs->u_regs[UREG_I1];
+	unsigned long orig_i1;
 	long ret;
-
 	struct kernel_clone_args args = {
 		.flags		= CLONE_VFORK | CLONE_VM,
 		.exit_signal	= SIGCHLD,
-		/* Reuse the parent's stack for the child. */
-		.stack		= regs->u_regs[UREG_FP],
 	};
 
+	synchronize_user_stack();
+
+	orig_i1 = regs->u_regs[UREG_I1];
+	/* Reuse the parent's stack for the child. */
+	args.stack = regs->u_regs[UREG_FP];
+
 	ret = kernel_clone(&args);
 
 	/* If we get an error and potentially restart the system
@@ -65,15 +72,18 @@ asmlinkage long sparc_vfork(struct pt_regs *regs)
 
 asmlinkage long sparc_clone(struct pt_regs *regs)
 {
-	unsigned long orig_i1 = regs->u_regs[UREG_I1];
-	unsigned int flags = lower_32_bits(regs->u_regs[UREG_I0]);
+	unsigned long orig_i1;
+	unsigned int flags;
 	long ret;
+	struct kernel_clone_args args = {0};
 
-	struct kernel_clone_args args = {
-		.flags		= (flags & ~CSIGNAL),
-		.exit_signal	= (flags & CSIGNAL),
-		.tls		= regs->u_regs[UREG_I3],
-	};
+	synchronize_user_stack();
+
+	orig_i1 = regs->u_regs[UREG_I1];
+	flags = lower_32_bits(regs->u_regs[UREG_I0]);
+	args.flags		= (flags & ~CSIGNAL);
+	args.exit_signal	= (flags & CSIGNAL);
+	args.tls		= regs->u_regs[UREG_I3];
 
 #ifdef CONFIG_COMPAT
 	if (test_thread_flag(TIF_32BIT)) {
-- 
2.35.3


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 2/2] sparc: Add architecture support for clone3
  2026-01-19 14:47 [PATCH v2 0/2] sparc: Add architecture support for clone3 Ludwig Rydberg
  2026-01-19 14:47 ` [PATCH v2 1/2] sparc: Synchronize user stack on fork and clone Ludwig Rydberg
@ 2026-01-19 14:47 ` Ludwig Rydberg
  2026-01-19 14:53   ` Arnd Bergmann
                     ` (2 more replies)
  2026-01-26 17:03 ` [PATCH v2 0/2] " Andreas Larsson
  2 siblings, 3 replies; 7+ messages in thread
From: Ludwig Rydberg @ 2026-01-19 14:47 UTC (permalink / raw)
  To: davem, andreas, brauner, shuah
  Cc: sparclinux, linux-kselftest, linux-kernel, arnd, glaubitz, geert,
	schuster.simon, jrtc27

Add support for the clone3 system call to the SPARC architectures.

The implementation follows the pattern of the original clone syscall.
However, instead of explicitly calling kernel_clone, the clone3
handler calls the generic sys_clone3 handler in kernel/fork.
In case no stack is provided, the parents stack is reused.

The return value convention for clone3 follows the regular kernel return
value convention (in contrast to the original clone/fork on SPARC).

Closes: https://github.com/sparclinux/issues/issues/10
Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com>
---
 arch/sparc/include/asm/syscalls.h      |  1 +
 arch/sparc/include/asm/unistd.h        |  2 --
 arch/sparc/kernel/entry.S              | 15 ++++++++++++++
 arch/sparc/kernel/kernel.h             |  1 +
 arch/sparc/kernel/process.c            | 14 +++++++++++++
 arch/sparc/kernel/process_32.c         | 23 ++++++++++++++++------
 arch/sparc/kernel/process_64.c         | 27 ++++++++++++++++++++------
 arch/sparc/kernel/syscalls.S           |  8 ++++++++
 arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
 9 files changed, 78 insertions(+), 15 deletions(-)

diff --git a/arch/sparc/include/asm/syscalls.h b/arch/sparc/include/asm/syscalls.h
index 35575fbfb9dc..282e62b66518 100644
--- a/arch/sparc/include/asm/syscalls.h
+++ b/arch/sparc/include/asm/syscalls.h
@@ -7,5 +7,6 @@ struct pt_regs;
 asmlinkage long sparc_fork(struct pt_regs *regs);
 asmlinkage long sparc_vfork(struct pt_regs *regs);
 asmlinkage long sparc_clone(struct pt_regs *regs);
+asmlinkage long sparc_clone3(struct pt_regs *regs);
 
 #endif /* _SPARC64_SYSCALLS_H */
diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h
index 3380411a4537..d6bc76706a7a 100644
--- a/arch/sparc/include/asm/unistd.h
+++ b/arch/sparc/include/asm/unistd.h
@@ -49,8 +49,6 @@
 #define __ARCH_WANT_COMPAT_STAT
 #endif
 
-#define __ARCH_BROKEN_SYS_CLONE3
-
 #ifdef __32bit_syscall_numbers__
 /* Sparc 32-bit only has the "setresuid32", "getresuid32" variants,
  * it never had the plain ones and there is no value to adding those
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index a3fdee4cd6fa..ea51ef52c952 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -907,6 +907,21 @@ flush_patch_four:
 	jmpl	%l1 + %lo(sparc_vfork), %g0
 	 add	%sp, STACKFRAME_SZ, %o0
 
+	.globl	__sys_clone3, flush_patch_five
+__sys_clone3:
+	mov	%o7, %l5
+flush_patch_five:
+	FLUSH_ALL_KERNEL_WINDOWS;
+	ld	[%curptr + TI_TASK], %o4
+	rd	%psr, %g4
+	WRITE_PAUSE
+	rd	%wim, %g5
+	WRITE_PAUSE
+	std	%g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
+	add	%sp, STACKFRAME_SZ, %o0
+	call	sparc_clone3
+	 mov	%l5, %o7
+
         .align  4
 linux_sparc_ni_syscall:
 	sethi   %hi(sys_ni_syscall), %l7
diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
index 8328a3b78a44..4ee85051521a 100644
--- a/arch/sparc/kernel/kernel.h
+++ b/arch/sparc/kernel/kernel.h
@@ -18,6 +18,7 @@ extern int ncpus_probed;
 asmlinkage long sparc_clone(struct pt_regs *regs);
 asmlinkage long sparc_fork(struct pt_regs *regs);
 asmlinkage long sparc_vfork(struct pt_regs *regs);
+asmlinkage long sparc_clone3(struct pt_regs *regs);
 
 #ifdef CONFIG_SPARC64
 /* setup_64.c */
diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
index 7d69877511fa..d72fa0665943 100644
--- a/arch/sparc/kernel/process.c
+++ b/arch/sparc/kernel/process.c
@@ -12,6 +12,7 @@
 #include <linux/sched/task.h>
 #include <linux/sched/task_stack.h>
 #include <linux/signal.h>
+#include <linux/syscalls.h>
 
 #include "kernel.h"
 
@@ -118,3 +119,16 @@ asmlinkage long sparc_clone(struct pt_regs *regs)
 
 	return ret;
 }
+
+asmlinkage long sparc_clone3(struct pt_regs *regs)
+{
+	unsigned long sz;
+	struct clone_args __user *cl_args;
+
+	synchronize_user_stack();
+
+	cl_args = (struct clone_args __user *)regs->u_regs[UREG_I0];
+	sz = regs->u_regs[UREG_I1];
+
+	return sys_clone3(cl_args, sz);
+}
diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
index 5a28c0e91bf1..dd8c6c02b0f1 100644
--- a/arch/sparc/kernel/process_32.c
+++ b/arch/sparc/kernel/process_32.c
@@ -247,6 +247,8 @@ clone_stackframe(struct sparc_stackf __user *dst,
  * Parent -->  %o0 == childs  pid, %o1 == 0
  * Child  -->  %o0 == parents pid, %o1 == 1
  *
+ * clone3() - Uses regular kernel return value conventions
+ *
  * NOTE: We have a separate fork kpsr/kwim because
  *       the parent could change these values between
  *       sys_fork invocation and when we reach here
@@ -261,11 +263,11 @@ extern void ret_from_kernel_thread(void);
 int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 {
 	u64 clone_flags = args->flags;
-	unsigned long sp = args->stack;
 	unsigned long tls = args->tls;
 	struct thread_info *ti = task_thread_info(p);
 	struct pt_regs *childregs, *regs = current_pt_regs();
 	char *new_stack;
+	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
 
 #ifndef CONFIG_SMP
 	if(last_task_used_math == current) {
@@ -350,13 +352,22 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 	childregs->psr &= ~PSR_EF;
 	clear_tsk_thread_flag(p, TIF_USEDFPU);
 #endif
+	/* Handle return value conventions */
+	if (regs->u_regs[UREG_G1] == __NR_clone3) {
+		/* clone3() - use regular kernel return value convention */
+
+		/* Set the return value for the child. */
+		childregs->u_regs[UREG_I0] = 0;
+	} else {
+		/* clone()/fork() - use SunOS return value convention */
 
-	/* Set the return value for the child. */
-	childregs->u_regs[UREG_I0] = current->pid;
-	childregs->u_regs[UREG_I1] = 1;
+		/* Set the return value for the child. */
+		childregs->u_regs[UREG_I0] = current->pid;
+		childregs->u_regs[UREG_I1] = 1;
 
-	/* Set the return value for the parent. */
-	regs->u_regs[UREG_I1] = 0;
+		/* Set the return value for the parent. */
+		regs->u_regs[UREG_I1] = 0;
+	}
 
 	if (clone_flags & CLONE_SETTLS)
 		childregs->u_regs[UREG_G7] = tls;
diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
index 25781923788a..e889da8e4835 100644
--- a/arch/sparc/kernel/process_64.c
+++ b/arch/sparc/kernel/process_64.c
@@ -564,17 +564,19 @@ void fault_in_user_windows(struct pt_regs *regs)
  * under SunOS are nothing short of bletcherous:
  * Parent -->  %o0 == childs  pid, %o1 == 0
  * Child  -->  %o0 == parents pid, %o1 == 1
+ *
+ * clone3() - Uses regular kernel return value conventions
  */
 int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 {
 	u64 clone_flags = args->flags;
-	unsigned long sp = args->stack;
 	unsigned long tls = args->tls;
 	struct thread_info *t = task_thread_info(p);
 	struct pt_regs *regs = current_pt_regs();
 	struct sparc_stackf *parent_sf;
 	unsigned long child_stack_sz;
 	char *child_trap_frame;
+	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
 
 	/* Calculate offset to stack_frame & pt_regs */
 	child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ);
@@ -616,12 +618,25 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 	if (t->utraps)
 		t->utraps[0]++;
 
-	/* Set the return value for the child. */
-	t->kregs->u_regs[UREG_I0] = current->pid;
-	t->kregs->u_regs[UREG_I1] = 1;
+	/* Handle return value conventions */
+	if (regs->u_regs[UREG_G1] == __NR_clone3) {
+		/* clone3() - use regular kernel return value convention */
+
+		/* Set the return value for the child. */
+		t->kregs->u_regs[UREG_I0] = 0;
+
+		/* Clear g1 to indicate user thread */
+		t->kregs->u_regs[UREG_G1] = 0;
+	} else {
+		/* clone()/fork() - use SunOS return value convention */
+
+		/* Set the return value for the child. */
+		t->kregs->u_regs[UREG_I0] = current->pid;
+		t->kregs->u_regs[UREG_I1] = 1;
 
-	/* Set the second return value for the parent. */
-	regs->u_regs[UREG_I1] = 0;
+		/* Set the second return value for the parent. */
+		regs->u_regs[UREG_I1] = 0;
+	}
 
 	if (clone_flags & CLONE_SETTLS)
 		t->kregs->u_regs[UREG_G7] = tls;
diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
index 0e8ab0602c36..96fe8763d70c 100644
--- a/arch/sparc/kernel/syscalls.S
+++ b/arch/sparc/kernel/syscalls.S
@@ -103,6 +103,12 @@ sys_clone:
 	ba,pt	%xcc, sparc_clone
 	 add	%sp, PTREGS_OFF, %o0
 
+	.align	32
+__sys_clone3:
+	flushw
+	ba,pt	%xcc, sparc_clone3
+	 add	%sp, PTREGS_OFF, %o0
+
 	.globl	ret_from_fork
 ret_from_fork:
 	/* Clear current_thread_info()->new_child. */
@@ -113,6 +119,8 @@ ret_from_fork:
 	brnz,pt	%o0, ret_sys_call
 	 ldx	[%g6 + TI_FLAGS], %l0
 	ldx	[%sp + PTREGS_OFF + PT_V9_G1], %l1
+	brz,pt	%l1, ret_sys_call
+	 nop
 	call	%l1
 	 ldx	[%sp + PTREGS_OFF + PT_V9_G2], %o0
 	ba,pt	%xcc, ret_sys_call
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 39aa26b6a50b..c0307bb09892 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -480,7 +480,7 @@
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
 434	common	pidfd_open			sys_pidfd_open
-# 435 reserved for clone3
+435	common	clone3				__sys_clone3
 436	common	close_range			sys_close_range
 437	common	openat2			sys_openat2
 438	common	pidfd_getfd			sys_pidfd_getfd
-- 
2.35.3


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 2/2] sparc: Add architecture support for clone3
  2026-01-19 14:47 ` [PATCH v2 2/2] sparc: Add architecture support for clone3 Ludwig Rydberg
@ 2026-01-19 14:53   ` Arnd Bergmann
  2026-01-20 18:36   ` John Paul Adrian Glaubitz
  2026-01-26 17:03   ` Andreas Larsson
  2 siblings, 0 replies; 7+ messages in thread
From: Arnd Bergmann @ 2026-01-19 14:53 UTC (permalink / raw)
  To: Ludwig Rydberg, David S . Miller, Andreas Larsson,
	Christian Brauner, shuah
  Cc: sparclinux, linux-kselftest, linux-kernel,
	John Paul Adrian Glaubitz, Geert Uytterhoeven, schuster.simon,
	Jessica Clarke

On Mon, Jan 19, 2026, at 15:47, Ludwig Rydberg wrote:
> Add support for the clone3 system call to the SPARC architectures.
>
> The implementation follows the pattern of the original clone syscall.
> However, instead of explicitly calling kernel_clone, the clone3
> handler calls the generic sys_clone3 handler in kernel/fork.
> In case no stack is provided, the parents stack is reused.
>
> The return value convention for clone3 follows the regular kernel return
> value convention (in contrast to the original clone/fork on SPARC).
>
> Closes: https://github.com/sparclinux/issues/issues/10
> Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com>

Thanks a lot for addressing this!

Acked-by: Arnd Bergmann <arnd@arndb.de>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 2/2] sparc: Add architecture support for clone3
  2026-01-19 14:47 ` [PATCH v2 2/2] sparc: Add architecture support for clone3 Ludwig Rydberg
  2026-01-19 14:53   ` Arnd Bergmann
@ 2026-01-20 18:36   ` John Paul Adrian Glaubitz
  2026-01-26 17:03   ` Andreas Larsson
  2 siblings, 0 replies; 7+ messages in thread
From: John Paul Adrian Glaubitz @ 2026-01-20 18:36 UTC (permalink / raw)
  To: Ludwig Rydberg, davem, andreas, brauner, shuah
  Cc: sparclinux, linux-kselftest, linux-kernel, arnd, geert,
	schuster.simon, jrtc27

Hi Ludwig,

On Mon, 2026-01-19 at 15:47 +0100, Ludwig Rydberg wrote:
> Add support for the clone3 system call to the SPARC architectures.
> 
> The implementation follows the pattern of the original clone syscall.
> However, instead of explicitly calling kernel_clone, the clone3
> handler calls the generic sys_clone3 handler in kernel/fork.
> In case no stack is provided, the parents stack is reused.
> 
> The return value convention for clone3 follows the regular kernel return
> value convention (in contrast to the original clone/fork on SPARC).
> 
> Closes: https://github.com/sparclinux/issues/issues/10
> Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com>
> ---
>  arch/sparc/include/asm/syscalls.h      |  1 +
>  arch/sparc/include/asm/unistd.h        |  2 --
>  arch/sparc/kernel/entry.S              | 15 ++++++++++++++
>  arch/sparc/kernel/kernel.h             |  1 +
>  arch/sparc/kernel/process.c            | 14 +++++++++++++
>  arch/sparc/kernel/process_32.c         | 23 ++++++++++++++++------
>  arch/sparc/kernel/process_64.c         | 27 ++++++++++++++++++++------
>  arch/sparc/kernel/syscalls.S           |  8 ++++++++
>  arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
>  9 files changed, 78 insertions(+), 15 deletions(-)
> 
> diff --git a/arch/sparc/include/asm/syscalls.h b/arch/sparc/include/asm/syscalls.h
> index 35575fbfb9dc..282e62b66518 100644
> --- a/arch/sparc/include/asm/syscalls.h
> +++ b/arch/sparc/include/asm/syscalls.h
> @@ -7,5 +7,6 @@ struct pt_regs;
>  asmlinkage long sparc_fork(struct pt_regs *regs);
>  asmlinkage long sparc_vfork(struct pt_regs *regs);
>  asmlinkage long sparc_clone(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>  
>  #endif /* _SPARC64_SYSCALLS_H */
> diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h
> index 3380411a4537..d6bc76706a7a 100644
> --- a/arch/sparc/include/asm/unistd.h
> +++ b/arch/sparc/include/asm/unistd.h
> @@ -49,8 +49,6 @@
>  #define __ARCH_WANT_COMPAT_STAT
>  #endif
>  
> -#define __ARCH_BROKEN_SYS_CLONE3
> -
>  #ifdef __32bit_syscall_numbers__
>  /* Sparc 32-bit only has the "setresuid32", "getresuid32" variants,
>   * it never had the plain ones and there is no value to adding those
> diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
> index a3fdee4cd6fa..ea51ef52c952 100644
> --- a/arch/sparc/kernel/entry.S
> +++ b/arch/sparc/kernel/entry.S
> @@ -907,6 +907,21 @@ flush_patch_four:
>  	jmpl	%l1 + %lo(sparc_vfork), %g0
>  	 add	%sp, STACKFRAME_SZ, %o0
>  
> +	.globl	__sys_clone3, flush_patch_five
> +__sys_clone3:
> +	mov	%o7, %l5
> +flush_patch_five:
> +	FLUSH_ALL_KERNEL_WINDOWS;
> +	ld	[%curptr + TI_TASK], %o4
> +	rd	%psr, %g4
> +	WRITE_PAUSE
> +	rd	%wim, %g5
> +	WRITE_PAUSE
> +	std	%g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
> +	add	%sp, STACKFRAME_SZ, %o0
> +	call	sparc_clone3
> +	 mov	%l5, %o7
> +
>          .align  4
>  linux_sparc_ni_syscall:
>  	sethi   %hi(sys_ni_syscall), %l7
> diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
> index 8328a3b78a44..4ee85051521a 100644
> --- a/arch/sparc/kernel/kernel.h
> +++ b/arch/sparc/kernel/kernel.h
> @@ -18,6 +18,7 @@ extern int ncpus_probed;
>  asmlinkage long sparc_clone(struct pt_regs *regs);
>  asmlinkage long sparc_fork(struct pt_regs *regs);
>  asmlinkage long sparc_vfork(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>  
>  #ifdef CONFIG_SPARC64
>  /* setup_64.c */
> diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
> index 7d69877511fa..d72fa0665943 100644
> --- a/arch/sparc/kernel/process.c
> +++ b/arch/sparc/kernel/process.c
> @@ -12,6 +12,7 @@
>  #include <linux/sched/task.h>
>  #include <linux/sched/task_stack.h>
>  #include <linux/signal.h>
> +#include <linux/syscalls.h>
>  
>  #include "kernel.h"
>  
> @@ -118,3 +119,16 @@ asmlinkage long sparc_clone(struct pt_regs *regs)
>  
>  	return ret;
>  }
> +
> +asmlinkage long sparc_clone3(struct pt_regs *regs)
> +{
> +	unsigned long sz;
> +	struct clone_args __user *cl_args;
> +
> +	synchronize_user_stack();
> +
> +	cl_args = (struct clone_args __user *)regs->u_regs[UREG_I0];
> +	sz = regs->u_regs[UREG_I1];
> +
> +	return sys_clone3(cl_args, sz);
> +}
> diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
> index 5a28c0e91bf1..dd8c6c02b0f1 100644
> --- a/arch/sparc/kernel/process_32.c
> +++ b/arch/sparc/kernel/process_32.c
> @@ -247,6 +247,8 @@ clone_stackframe(struct sparc_stackf __user *dst,
>   * Parent -->  %o0 == childs  pid, %o1 == 0
>   * Child  -->  %o0 == parents pid, %o1 == 1
>   *
> + * clone3() - Uses regular kernel return value conventions
> + *
>   * NOTE: We have a separate fork kpsr/kwim because
>   *       the parent could change these values between
>   *       sys_fork invocation and when we reach here
> @@ -261,11 +263,11 @@ extern void ret_from_kernel_thread(void);
>  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  {
>  	u64 clone_flags = args->flags;
> -	unsigned long sp = args->stack;
>  	unsigned long tls = args->tls;
>  	struct thread_info *ti = task_thread_info(p);
>  	struct pt_regs *childregs, *regs = current_pt_regs();
>  	char *new_stack;
> +	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>  
>  #ifndef CONFIG_SMP
>  	if(last_task_used_math == current) {
> @@ -350,13 +352,22 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  	childregs->psr &= ~PSR_EF;
>  	clear_tsk_thread_flag(p, TIF_USEDFPU);
>  #endif
> +	/* Handle return value conventions */
> +	if (regs->u_regs[UREG_G1] == __NR_clone3) {
> +		/* clone3() - use regular kernel return value convention */
> +
> +		/* Set the return value for the child. */
> +		childregs->u_regs[UREG_I0] = 0;
> +	} else {
> +		/* clone()/fork() - use SunOS return value convention */
>  
> -	/* Set the return value for the child. */
> -	childregs->u_regs[UREG_I0] = current->pid;
> -	childregs->u_regs[UREG_I1] = 1;
> +		/* Set the return value for the child. */
> +		childregs->u_regs[UREG_I0] = current->pid;
> +		childregs->u_regs[UREG_I1] = 1;
>  
> -	/* Set the return value for the parent. */
> -	regs->u_regs[UREG_I1] = 0;
> +		/* Set the return value for the parent. */
> +		regs->u_regs[UREG_I1] = 0;
> +	}
>  
>  	if (clone_flags & CLONE_SETTLS)
>  		childregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
> index 25781923788a..e889da8e4835 100644
> --- a/arch/sparc/kernel/process_64.c
> +++ b/arch/sparc/kernel/process_64.c
> @@ -564,17 +564,19 @@ void fault_in_user_windows(struct pt_regs *regs)
>   * under SunOS are nothing short of bletcherous:
>   * Parent -->  %o0 == childs  pid, %o1 == 0
>   * Child  -->  %o0 == parents pid, %o1 == 1
> + *
> + * clone3() - Uses regular kernel return value conventions
>   */
>  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  {
>  	u64 clone_flags = args->flags;
> -	unsigned long sp = args->stack;
>  	unsigned long tls = args->tls;
>  	struct thread_info *t = task_thread_info(p);
>  	struct pt_regs *regs = current_pt_regs();
>  	struct sparc_stackf *parent_sf;
>  	unsigned long child_stack_sz;
>  	char *child_trap_frame;
> +	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>  
>  	/* Calculate offset to stack_frame & pt_regs */
>  	child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ);
> @@ -616,12 +618,25 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  	if (t->utraps)
>  		t->utraps[0]++;
>  
> -	/* Set the return value for the child. */
> -	t->kregs->u_regs[UREG_I0] = current->pid;
> -	t->kregs->u_regs[UREG_I1] = 1;
> +	/* Handle return value conventions */
> +	if (regs->u_regs[UREG_G1] == __NR_clone3) {
> +		/* clone3() - use regular kernel return value convention */
> +
> +		/* Set the return value for the child. */
> +		t->kregs->u_regs[UREG_I0] = 0;
> +
> +		/* Clear g1 to indicate user thread */
> +		t->kregs->u_regs[UREG_G1] = 0;
> +	} else {
> +		/* clone()/fork() - use SunOS return value convention */
> +
> +		/* Set the return value for the child. */
> +		t->kregs->u_regs[UREG_I0] = current->pid;
> +		t->kregs->u_regs[UREG_I1] = 1;
>  
> -	/* Set the second return value for the parent. */
> -	regs->u_regs[UREG_I1] = 0;
> +		/* Set the second return value for the parent. */
> +		regs->u_regs[UREG_I1] = 0;
> +	}
>  
>  	if (clone_flags & CLONE_SETTLS)
>  		t->kregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
> index 0e8ab0602c36..96fe8763d70c 100644
> --- a/arch/sparc/kernel/syscalls.S
> +++ b/arch/sparc/kernel/syscalls.S
> @@ -103,6 +103,12 @@ sys_clone:
>  	ba,pt	%xcc, sparc_clone
>  	 add	%sp, PTREGS_OFF, %o0
>  
> +	.align	32
> +__sys_clone3:
> +	flushw
> +	ba,pt	%xcc, sparc_clone3
> +	 add	%sp, PTREGS_OFF, %o0
> +
>  	.globl	ret_from_fork
>  ret_from_fork:
>  	/* Clear current_thread_info()->new_child. */
> @@ -113,6 +119,8 @@ ret_from_fork:
>  	brnz,pt	%o0, ret_sys_call
>  	 ldx	[%g6 + TI_FLAGS], %l0
>  	ldx	[%sp + PTREGS_OFF + PT_V9_G1], %l1
> +	brz,pt	%l1, ret_sys_call
> +	 nop
>  	call	%l1
>  	 ldx	[%sp + PTREGS_OFF + PT_V9_G2], %o0
>  	ba,pt	%xcc, ret_sys_call
> diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
> index 39aa26b6a50b..c0307bb09892 100644
> --- a/arch/sparc/kernel/syscalls/syscall.tbl
> +++ b/arch/sparc/kernel/syscalls/syscall.tbl
> @@ -480,7 +480,7 @@
>  432	common	fsmount				sys_fsmount
>  433	common	fspick				sys_fspick
>  434	common	pidfd_open			sys_pidfd_open
> -# 435 reserved for clone3
> +435	common	clone3				__sys_clone3
>  436	common	close_range			sys_close_range
>  437	common	openat2			sys_openat2
>  438	common	pidfd_getfd			sys_pidfd_getfd

I can confirm that this patch passes both the clone3 selftest from within the
kernel sources in ./tools/testing/selftests/clone3:

root@raverin:/usr/src/linux/tools/testing/selftests/clone3# uname -a
Linux raverin 6.19.0-rc6+ #21 Tue Jan 20 12:41:03 UTC 2026 sparc64 GNU/Linux
root@raverin:/usr/src/linux/tools/testing/selftests/clone3# ./clone3
TAP version 13
1..19
# clone3() syscall supported
# Running test 'simple clone3()'
# [1495] Trying clone3() with flags 0 (size 0)
# I am the parent (1495). My child's pid is 1496
# I am the child, my PID is 1496
# [1495] clone3() with flags says: 0 expected 0
ok 1 simple clone3()
# Running test 'clone3() in a new PID_NS'
# [1495] Trying clone3() with flags 0x20000000 (size 0)
# I am the parent (1495). My child's pid is 1497
# I am the child, my PID is 1
# [1495] clone3() with flags says: 0 expected 0
ok 2 clone3() in a new PID_NS
# Running test 'CLONE_ARGS_SIZE_VER0'
# [1495] Trying clone3() with flags 0 (size 64)
# I am the parent (1495). My child's pid is 1498
# I am the child, my PID is 1498
# [1495] clone3() with flags says: 0 expected 0
ok 3 CLONE_ARGS_SIZE_VER0
# Running test 'CLONE_ARGS_SIZE_VER0 - 8'
# [1495] Trying clone3() with flags 0 (size 56)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 4 CLONE_ARGS_SIZE_VER0 - 8
# Running test 'sizeof(struct clone_args) + 8'
# [1495] Trying clone3() with flags 0 (size 96)
# I am the parent (1495). My child's pid is 1499
# I am the child, my PID is 1499
# [1495] clone3() with flags says: 0 expected 0
ok 5 sizeof(struct clone_args) + 8
# Running test 'exit_signal with highest 32 bits non-zero'
# [1495] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 6 exit_signal with highest 32 bits non-zero
# Running test 'negative 32-bit exit_signal'
# [1495] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 7 negative 32-bit exit_signal
# Running test 'exit_signal not fitting into CSIGNAL mask'
# [1495] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 8 exit_signal not fitting into CSIGNAL mask
# Running test 'NSIG < exit_signal < CSIG'
# [1495] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 9 NSIG < exit_signal < CSIG
# Running test 'Arguments sizeof(struct clone_args) + 8'
# [1495] Trying clone3() with flags 0 (size 96)
# I am the parent (1495). My child's pid is 1500
# I am the child, my PID is 1500
# [1495] clone3() with flags says: 0 expected 0
ok 10 Arguments sizeof(struct clone_args) + 8
# Running test 'Arguments sizeof(struct clone_args) + 16'
# [1495] Trying clone3() with flags 0 (size 104)
# Argument list too long - Failed to create new process
# [1495] clone3() with flags says: -7 expected -7
ok 11 Arguments sizeof(struct clone_args) + 16
# Running test 'Arguments sizeof(struct clone_arg) * 2'
# [1495] Trying clone3() with flags 0 (size 104)
# Argument list too long - Failed to create new process
# [1495] clone3() with flags says: -7 expected -7
ok 12 Arguments sizeof(struct clone_arg) * 2
# Running test 'Arguments > page size'
# [1495] Trying clone3() with flags 0 (size 8200)
# Argument list too long - Failed to create new process
# [1495] clone3() with flags says: -7 expected -7
ok 13 Arguments > page size
# Running test 'CLONE_ARGS_SIZE_VER0 in a new PID NS'
# [1495] Trying clone3() with flags 0x20000000 (size 64)
# I am the parent (1495). My child's pid is 1501
# I am the child, my PID is 1
# [1495] clone3() with flags says: 0 expected 0
ok 14 CLONE_ARGS_SIZE_VER0 in a new PID NS
# Running test 'CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS'
# [1495] Trying clone3() with flags 0x20000000 (size 56)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 15 CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS
# Running test 'sizeof(struct clone_args) + 8 in a new PID NS'
# [1495] Trying clone3() with flags 0x20000000 (size 96)
# I am the parent (1495). My child's pid is 1502
# I am the child, my PID is 1
# [1495] clone3() with flags says: 0 expected 0
ok 16 sizeof(struct clone_args) + 8 in a new PID NS
# Running test 'Arguments > page size in a new PID NS'
# [1495] Trying clone3() with flags 0x20000000 (size 8200)
# Argument list too long - Failed to create new process
# [1495] clone3() with flags says: -7 expected -7
ok 17 Arguments > page size in a new PID NS
# Time namespaces are not supported
ok 18 # SKIP New time NS
# Running test 'exit signal (SIGCHLD) in flags'
# [1495] Trying clone3() with flags 0x14 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 19 exit signal (SIGCHLD) in flags
# 1 skipped test(s) detected. Consider enabling relevant config options to improve coverage.
# Totals: pass:18 fail:0 xfail:0 xpass:0 skip:1 error:0
root@raverin:/usr/src/linux/tools/testing/selftests/clone3#

and the libcamera build and testsuite:

Ok:                38
Expected Fail:     1
Fail:              0
Skipped:           31

Tested-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>

Adrian

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 2/2] sparc: Add architecture support for clone3
  2026-01-19 14:47 ` [PATCH v2 2/2] sparc: Add architecture support for clone3 Ludwig Rydberg
  2026-01-19 14:53   ` Arnd Bergmann
  2026-01-20 18:36   ` John Paul Adrian Glaubitz
@ 2026-01-26 17:03   ` Andreas Larsson
  2 siblings, 0 replies; 7+ messages in thread
From: Andreas Larsson @ 2026-01-26 17:03 UTC (permalink / raw)
  To: Ludwig Rydberg, davem, brauner, shuah
  Cc: sparclinux, linux-kselftest, linux-kernel, arnd, glaubitz, geert,
	schuster.simon, jrtc27

On 2026-01-19 15:47, Ludwig Rydberg wrote:
> Add support for the clone3 system call to the SPARC architectures.
> 
> The implementation follows the pattern of the original clone syscall.
> However, instead of explicitly calling kernel_clone, the clone3
> handler calls the generic sys_clone3 handler in kernel/fork.
> In case no stack is provided, the parents stack is reused.
> 
> The return value convention for clone3 follows the regular kernel return
> value convention (in contrast to the original clone/fork on SPARC).
> 
> Closes: https://github.com/sparclinux/issues/issues/10
> Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com>
> ---
>  arch/sparc/include/asm/syscalls.h      |  1 +
>  arch/sparc/include/asm/unistd.h        |  2 --
>  arch/sparc/kernel/entry.S              | 15 ++++++++++++++
>  arch/sparc/kernel/kernel.h             |  1 +
>  arch/sparc/kernel/process.c            | 14 +++++++++++++
>  arch/sparc/kernel/process_32.c         | 23 ++++++++++++++++------
>  arch/sparc/kernel/process_64.c         | 27 ++++++++++++++++++++------
>  arch/sparc/kernel/syscalls.S           |  8 ++++++++
>  arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
>  9 files changed, 78 insertions(+), 15 deletions(-)
> 
> diff --git a/arch/sparc/include/asm/syscalls.h b/arch/sparc/include/asm/syscalls.h
> index 35575fbfb9dc..282e62b66518 100644
> --- a/arch/sparc/include/asm/syscalls.h
> +++ b/arch/sparc/include/asm/syscalls.h
> @@ -7,5 +7,6 @@ struct pt_regs;
>  asmlinkage long sparc_fork(struct pt_regs *regs);
>  asmlinkage long sparc_vfork(struct pt_regs *regs);
>  asmlinkage long sparc_clone(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>  
>  #endif /* _SPARC64_SYSCALLS_H */
> diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h
> index 3380411a4537..d6bc76706a7a 100644
> --- a/arch/sparc/include/asm/unistd.h
> +++ b/arch/sparc/include/asm/unistd.h
> @@ -49,8 +49,6 @@
>  #define __ARCH_WANT_COMPAT_STAT
>  #endif
>  
> -#define __ARCH_BROKEN_SYS_CLONE3
> -
>  #ifdef __32bit_syscall_numbers__
>  /* Sparc 32-bit only has the "setresuid32", "getresuid32" variants,
>   * it never had the plain ones and there is no value to adding those
> diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
> index a3fdee4cd6fa..ea51ef52c952 100644
> --- a/arch/sparc/kernel/entry.S
> +++ b/arch/sparc/kernel/entry.S
> @@ -907,6 +907,21 @@ flush_patch_four:
>  	jmpl	%l1 + %lo(sparc_vfork), %g0
>  	 add	%sp, STACKFRAME_SZ, %o0
>  
> +	.globl	__sys_clone3, flush_patch_five
> +__sys_clone3:
> +	mov	%o7, %l5
> +flush_patch_five:
> +	FLUSH_ALL_KERNEL_WINDOWS;
> +	ld	[%curptr + TI_TASK], %o4
> +	rd	%psr, %g4
> +	WRITE_PAUSE
> +	rd	%wim, %g5
> +	WRITE_PAUSE
> +	std	%g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
> +	add	%sp, STACKFRAME_SZ, %o0
> +	call	sparc_clone3
> +	 mov	%l5, %o7
> +
>          .align  4
>  linux_sparc_ni_syscall:
>  	sethi   %hi(sys_ni_syscall), %l7
> diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
> index 8328a3b78a44..4ee85051521a 100644
> --- a/arch/sparc/kernel/kernel.h
> +++ b/arch/sparc/kernel/kernel.h
> @@ -18,6 +18,7 @@ extern int ncpus_probed;
>  asmlinkage long sparc_clone(struct pt_regs *regs);
>  asmlinkage long sparc_fork(struct pt_regs *regs);
>  asmlinkage long sparc_vfork(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>  
>  #ifdef CONFIG_SPARC64
>  /* setup_64.c */
> diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
> index 7d69877511fa..d72fa0665943 100644
> --- a/arch/sparc/kernel/process.c
> +++ b/arch/sparc/kernel/process.c
> @@ -12,6 +12,7 @@
>  #include <linux/sched/task.h>
>  #include <linux/sched/task_stack.h>
>  #include <linux/signal.h>
> +#include <linux/syscalls.h>
>  
>  #include "kernel.h"
>  
> @@ -118,3 +119,16 @@ asmlinkage long sparc_clone(struct pt_regs *regs)
>  
>  	return ret;
>  }
> +
> +asmlinkage long sparc_clone3(struct pt_regs *regs)
> +{
> +	unsigned long sz;
> +	struct clone_args __user *cl_args;
> +
> +	synchronize_user_stack();
> +
> +	cl_args = (struct clone_args __user *)regs->u_regs[UREG_I0];
> +	sz = regs->u_regs[UREG_I1];
> +
> +	return sys_clone3(cl_args, sz);
> +}
> diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
> index 5a28c0e91bf1..dd8c6c02b0f1 100644
> --- a/arch/sparc/kernel/process_32.c
> +++ b/arch/sparc/kernel/process_32.c
> @@ -247,6 +247,8 @@ clone_stackframe(struct sparc_stackf __user *dst,
>   * Parent -->  %o0 == childs  pid, %o1 == 0
>   * Child  -->  %o0 == parents pid, %o1 == 1
>   *
> + * clone3() - Uses regular kernel return value conventions
> + *
>   * NOTE: We have a separate fork kpsr/kwim because
>   *       the parent could change these values between
>   *       sys_fork invocation and when we reach here
> @@ -261,11 +263,11 @@ extern void ret_from_kernel_thread(void);
>  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  {
>  	u64 clone_flags = args->flags;
> -	unsigned long sp = args->stack;
>  	unsigned long tls = args->tls;
>  	struct thread_info *ti = task_thread_info(p);
>  	struct pt_regs *childregs, *regs = current_pt_regs();
>  	char *new_stack;
> +	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>  
>  #ifndef CONFIG_SMP
>  	if(last_task_used_math == current) {
> @@ -350,13 +352,22 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  	childregs->psr &= ~PSR_EF;
>  	clear_tsk_thread_flag(p, TIF_USEDFPU);
>  #endif
> +	/* Handle return value conventions */
> +	if (regs->u_regs[UREG_G1] == __NR_clone3) {
> +		/* clone3() - use regular kernel return value convention */
> +
> +		/* Set the return value for the child. */
> +		childregs->u_regs[UREG_I0] = 0;
> +	} else {
> +		/* clone()/fork() - use SunOS return value convention */
>  
> -	/* Set the return value for the child. */
> -	childregs->u_regs[UREG_I0] = current->pid;
> -	childregs->u_regs[UREG_I1] = 1;
> +		/* Set the return value for the child. */
> +		childregs->u_regs[UREG_I0] = current->pid;
> +		childregs->u_regs[UREG_I1] = 1;
>  
> -	/* Set the return value for the parent. */
> -	regs->u_regs[UREG_I1] = 0;
> +		/* Set the return value for the parent. */
> +		regs->u_regs[UREG_I1] = 0;
> +	}
>  
>  	if (clone_flags & CLONE_SETTLS)
>  		childregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
> index 25781923788a..e889da8e4835 100644
> --- a/arch/sparc/kernel/process_64.c
> +++ b/arch/sparc/kernel/process_64.c
> @@ -564,17 +564,19 @@ void fault_in_user_windows(struct pt_regs *regs)
>   * under SunOS are nothing short of bletcherous:
>   * Parent -->  %o0 == childs  pid, %o1 == 0
>   * Child  -->  %o0 == parents pid, %o1 == 1
> + *
> + * clone3() - Uses regular kernel return value conventions
>   */
>  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  {
>  	u64 clone_flags = args->flags;
> -	unsigned long sp = args->stack;
>  	unsigned long tls = args->tls;
>  	struct thread_info *t = task_thread_info(p);
>  	struct pt_regs *regs = current_pt_regs();
>  	struct sparc_stackf *parent_sf;
>  	unsigned long child_stack_sz;
>  	char *child_trap_frame;
> +	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>  
>  	/* Calculate offset to stack_frame & pt_regs */
>  	child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ);
> @@ -616,12 +618,25 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  	if (t->utraps)
>  		t->utraps[0]++;
>  
> -	/* Set the return value for the child. */
> -	t->kregs->u_regs[UREG_I0] = current->pid;
> -	t->kregs->u_regs[UREG_I1] = 1;
> +	/* Handle return value conventions */
> +	if (regs->u_regs[UREG_G1] == __NR_clone3) {
> +		/* clone3() - use regular kernel return value convention */
> +
> +		/* Set the return value for the child. */
> +		t->kregs->u_regs[UREG_I0] = 0;
> +
> +		/* Clear g1 to indicate user thread */
> +		t->kregs->u_regs[UREG_G1] = 0;
> +	} else {
> +		/* clone()/fork() - use SunOS return value convention */
> +
> +		/* Set the return value for the child. */
> +		t->kregs->u_regs[UREG_I0] = current->pid;
> +		t->kregs->u_regs[UREG_I1] = 1;
>  
> -	/* Set the second return value for the parent. */
> -	regs->u_regs[UREG_I1] = 0;
> +		/* Set the second return value for the parent. */
> +		regs->u_regs[UREG_I1] = 0;
> +	}
>  
>  	if (clone_flags & CLONE_SETTLS)
>  		t->kregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
> index 0e8ab0602c36..96fe8763d70c 100644
> --- a/arch/sparc/kernel/syscalls.S
> +++ b/arch/sparc/kernel/syscalls.S
> @@ -103,6 +103,12 @@ sys_clone:
>  	ba,pt	%xcc, sparc_clone
>  	 add	%sp, PTREGS_OFF, %o0
>  
> +	.align	32
> +__sys_clone3:
> +	flushw
> +	ba,pt	%xcc, sparc_clone3
> +	 add	%sp, PTREGS_OFF, %o0
> +
>  	.globl	ret_from_fork
>  ret_from_fork:
>  	/* Clear current_thread_info()->new_child. */
> @@ -113,6 +119,8 @@ ret_from_fork:
>  	brnz,pt	%o0, ret_sys_call
>  	 ldx	[%g6 + TI_FLAGS], %l0
>  	ldx	[%sp + PTREGS_OFF + PT_V9_G1], %l1
> +	brz,pt	%l1, ret_sys_call
> +	 nop
>  	call	%l1
>  	 ldx	[%sp + PTREGS_OFF + PT_V9_G2], %o0
>  	ba,pt	%xcc, ret_sys_call
> diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
> index 39aa26b6a50b..c0307bb09892 100644
> --- a/arch/sparc/kernel/syscalls/syscall.tbl
> +++ b/arch/sparc/kernel/syscalls/syscall.tbl
> @@ -480,7 +480,7 @@
>  432	common	fsmount				sys_fsmount
>  433	common	fspick				sys_fspick
>  434	common	pidfd_open			sys_pidfd_open
> -# 435 reserved for clone3
> +435	common	clone3				__sys_clone3
>  436	common	close_range			sys_close_range
>  437	common	openat2			sys_openat2
>  438	common	pidfd_getfd			sys_pidfd_getfd

Reviewed-by: Andreas Larsson <andreas@gaisler.com>
Tested-by: Andreas Larsson <andreas@gaisler.com>

Thanks,
Andreas


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 0/2] sparc: Add architecture support for clone3
  2026-01-19 14:47 [PATCH v2 0/2] sparc: Add architecture support for clone3 Ludwig Rydberg
  2026-01-19 14:47 ` [PATCH v2 1/2] sparc: Synchronize user stack on fork and clone Ludwig Rydberg
  2026-01-19 14:47 ` [PATCH v2 2/2] sparc: Add architecture support for clone3 Ludwig Rydberg
@ 2026-01-26 17:03 ` Andreas Larsson
  2 siblings, 0 replies; 7+ messages in thread
From: Andreas Larsson @ 2026-01-26 17:03 UTC (permalink / raw)
  To: Ludwig Rydberg, davem, brauner, shuah
  Cc: sparclinux, linux-kselftest, linux-kernel, arnd, glaubitz, geert,
	schuster.simon, jrtc27

On 2026-01-19 15:47, Ludwig Rydberg wrote:
> Hi all,
> 
> This series adds support for the clone3 system call to the SPARC{32|64}
> architectures and also adds a related patch for clone/fork/vfork that fix an
> issue previously reported[1] that could result in -EFAULT for no good reason.
> Without this patch, the clone3 system call would need the same mitigation as
> introduced in glibc[2] for the clone system call.
> 
> About "sparc: Synchronize user stack on fork and clone"
> ---------------------------------------------------------
> 
> The clone3 implementation is developed on top of a fix for an issue reported
> by Adrian Glaubitz[1], where a clone call could return -EFAULT. This problem
> has since been mitigated in glibc[2] by synchronizing the user stack before
> calling clone.
> 
> The root cause analysis of the kernel side when running the program in [1]
> shows that the window spill handler routine on both SPARC{32|64} is unable to
> flush a user window to the stack (due to MMU related faults) when flushing all
> windows before handling the syscall. This then results in a -EFAULT when
> copy_thread() fails to clone the uncommitted stackframe of the parent.
> 
> For SPARC32:
> Prior to calling the syscall wrappers for clone/fork/vork all windows are
> flushed by a macro (FLUSH_ALL_KERNEL_WINDOWS).
> In the window spill trap handler, MMU fault-handling is temporarily
> disabled while storing the window. If the window can't be stored
> (which normally would have triggered a fault trap) the routine
> backups the user window and increments a thread counter (wsaved).
> 
> For SPARC64:
> Prior to calling the syscall wrappers for clone/fork/vork all windows are
> flushed by issuing the flushw instruction.
> In the window spill trap handler, if an exception triggers, then the user
> window is added to the thread's user window buffer (in kernel memory) and
> a thread counter (wsaved) counter is incremented.
> 
> Both SPARC{32|64}:
> Eventually copy_thread will be called, which then will fail to clone the parent
> stackframe to the child as the user window has not been flushed to the stack.
> 
> Fixed by adding a call to synchronize_user_stack() prior to calling
> kernel_clone(). The patch has been tested both with and without the mitigation
> in glibc by running the program mentioned in [1].
> 
> SPARC32:
> - Tested in QEMU emulating sun4m using Buildroot 2025.02
>   (qemu_sparc_ss10_defconfig).
> - Tested on LEON using a GR-CPCI-GR740 development board from
>   Frontgrade Gaisler.
> 
> SPARC64:
> - Tested in QEMU emulating sun4u using Buildroot 2025.02
>   (qemu_sparc64_sun4u_defconfig).
> 
> About the clone3 implementation:
> --------------------------------
> 
> The implementation in the architectural port follows the same pattern as for the
> original clone syscall. But instead of explicitly calling kernel_clone (as in
> sparc_clone) the clone3 handler calls the generic sys_clone3 handler
> (in kernel/fork). To get this to work without a user provided stack,
> the copy_thread functions had to be updated to handle cl_args.stack == NULL.
> In this case the stack of the parent is re-used.
> 
> The return value convention for clone3 follows the regular kernel return
> value convention (in contrast to the original clone/fork on SPARC).
> 
> In sparc_{clone|vfork|fork} there is a workaround due to the SunOS
> return value convention which is not needed for clone3.
> 
> When applying the patch series on top of v6.19-rc1 the relevant clone3 tests of
> kselftest pass:
> 
> # /usr/lib/kselftests/run_kselftest.sh -c clone3 -s
> kselftest: Running tests in clone3
> TAP version 13
> 1..4
> # selftests: clone3: clone3
> ok 1 selftests: clone3: clone3
> # selftests: clone3: clone3_clear_sighand
> ok 2 selftests: clone3: clone3_clear_sighand
> # selftests: clone3: clone3_set_tid
> ok 3 selftests: clone3: clone3_set_tid
> # selftests: clone3: clone3_cap_checkpoint_restore
> ok 4 selftests: clone3: clone3_cap_checkpoint_restore
> 
> Note that the clone3_cap_checkpoint test failed in the same way as mentioned in
> [3] (due to incompatibility with the libcap version on my system).
> When applying the patch from [4] or by downgrading libcap to 2.59 the test pass.
> 
> SPARC32:
> - Tested in QEMU emulating sun4m using Buildroot 2025.02
>   (qemu_sparc_ss10_defconfig).
> - Tested on LEON using a GR-CPCI-GR740 development board from
>   Frontgrade Gaisler.
> 
> SPARC64:
> - Tested in QEMU emulating sun4u using Buildroot 2025.02
>   (qemu_sparc64_sun4u_defconfig).
> - Tested on UltraSparc T4
> 
> [1]: https://lore.kernel.org/sparclinux/3ae4130c-c5aa-428e-b819-44cf2daf2af1@mkarcher.dialup.fu-berlin.de/
> [2]: https://sourceware.org/bugzilla/show_bug.cgi?id=31394
> [3]: https://lore.kernel.org/all/20250901-nios2-implement-clone3-v2-0-53fcf5577d57@siemens-energy.com/
> [4]: https://lore.kernel.org/all/20241105062948.1037011-1-zhouyuhang1010@163.com/
> 
> ---
> Changes in v2:
> - Added Tested-by tag from Adrian Glaubitz
> - Implement generic Linux return value convention for clone3
> - Drop SPARC handling for sys_clone3 in selftests
> - Remove legacy ABI return value handling in sparc_clone3
> - Link to v1: https://lore.kernel.org/sparclinux/20260116153051.21678-1-ludwig.rydberg@gaisler.com/
> ---
> 
> Andreas Larsson (1):
>   sparc: Synchronize user stack on fork and clone
> 
> Ludwig Rydberg (1):
>   sparc: Add architecture support for clone3
> 
>  arch/sparc/include/asm/syscalls.h      |  1 +
>  arch/sparc/include/asm/unistd.h        |  2 -
>  arch/sparc/kernel/entry.S              | 15 ++++++++
>  arch/sparc/kernel/kernel.h             |  1 +
>  arch/sparc/kernel/process.c            | 52 +++++++++++++++++++-------
>  arch/sparc/kernel/process_32.c         | 23 +++++++++---
>  arch/sparc/kernel/process_64.c         | 27 ++++++++++---
>  arch/sparc/kernel/syscalls.S           |  8 ++++
>  arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
>  9 files changed, 102 insertions(+), 29 deletions(-)
> 
> 
> base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8

Picking the series up to my for-next.

Thanks,
Andreas


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-01-26 17:03 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-19 14:47 [PATCH v2 0/2] sparc: Add architecture support for clone3 Ludwig Rydberg
2026-01-19 14:47 ` [PATCH v2 1/2] sparc: Synchronize user stack on fork and clone Ludwig Rydberg
2026-01-19 14:47 ` [PATCH v2 2/2] sparc: Add architecture support for clone3 Ludwig Rydberg
2026-01-19 14:53   ` Arnd Bergmann
2026-01-20 18:36   ` John Paul Adrian Glaubitz
2026-01-26 17:03   ` Andreas Larsson
2026-01-26 17:03 ` [PATCH v2 0/2] " Andreas Larsson

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

all inboxes | Powered by JetHome®