Commit e38eba3b authored by Andreas Larsson's avatar Andreas Larsson
Browse files

sparc: Synchronize user stack on fork and clone

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: default avatarAndreas Larsson <andreas@gaisler.com>
Signed-off-by: default avatarLudwig Rydberg <ludwig.rydberg@gaisler.com>
Tested-by: default avatarJohn Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Link: https://lore.kernel.org/r/20260119144753.27945-2-ludwig.rydberg@gaisler.com


Signed-off-by: default avatarAndreas Larsson <andreas@gaisler.com>
parent dc2f4d41
Loading
Loading
Loading
Loading
+24 −14
Original line number Diff line number Diff line
@@ -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)) {