| From 7b2ce60e39b6a4141464d72ae46f407c4cabe928 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 19 Jan 2026 15:47:52 +0100 |
| Subject: sparc: Synchronize user stack on fork and clone |
| |
| From: Andreas Larsson <andreas@gaisler.com> |
| |
| [ Upstream commit e38eba3b77878ada327a572a41596a3b0b44e522 ] |
| |
| 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> |
| Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com> |
| Tested-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> |
| Link: https://lore.kernel.org/r/20260119144753.27945-2-ludwig.rydberg@gaisler.com |
| Signed-off-by: Andreas Larsson <andreas@gaisler.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| 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 0442ab00518d3..7d69877511fac 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.51.0 |
| |