Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1daeaa31 authored by Brian Gerst's avatar Brian Gerst Committed by Ingo Molnar
Browse files

x86/asm/entry: Fix execve() and sigreturn() syscalls to always return via IRET



Both the execve() and sigreturn() family of syscalls have the
ability to change registers in ways that may not be compatabile
with the syscall path they were called from.

In particular, SYSRET and SYSEXIT can't handle non-default %cs and %ss,
and some bits in eflags.

These syscalls have stubs that are hardcoded to jump to the IRET path,
and not return to the original syscall path.

The following commit:

   76f5df43 ("Always allocate a complete "struct pt_regs" on the kernel stack")

recently changed this for some 32-bit compat syscalls, but introduced a bug where
execve from a 32-bit program to a 64-bit program would fail because it still returned
via SYSRETL. This caused Wine to fail when built for both 32-bit and 64-bit.

This patch sets TIF_NOTIFY_RESUME for execve() and sigreturn() so
that the IRET path is always taken on exit to userspace.

Signed-off-by: default avatarBrian Gerst <brgerst@gmail.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/1426978461-32089-1-git-send-email-brgerst@gmail.com


[ Improved the changelog and comments. ]
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent c38e5038
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -203,6 +203,8 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,

	err |= restore_xstate_sig(buf, 1);

	force_iret();

	return err;
}

+1 −1
Original line number Diff line number Diff line
@@ -251,7 +251,7 @@ static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
 */
#define arch_ptrace_stop_needed(code, info)				\
({									\
	set_thread_flag(TIF_NOTIFY_RESUME);				\
	force_iret();							\
	false;								\
})

+10 −0
Original line number Diff line number Diff line
@@ -260,6 +260,16 @@ static inline bool is_ia32_task(void)
#endif
	return false;
}

/*
 * Force syscall return via IRET by making it look as if there was
 * some work pending. IRET is our most capable (but slowest) syscall
 * return path, which is able to restore modified SS, CS and certain
 * EFLAGS values that other (fast) syscall return instructions
 * are not able to restore properly.
 */
#define force_iret() set_thread_flag(TIF_NOTIFY_RESUME)

#endif	/* !__ASSEMBLY__ */

#ifndef __ASSEMBLY__
+1 −5
Original line number Diff line number Diff line
@@ -206,11 +206,7 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
	regs->ip		= new_ip;
	regs->sp		= new_sp;
	regs->flags		= X86_EFLAGS_IF;
	/*
	 * force it to the iret return path by making it look as if there was
	 * some work pending.
	 */
	set_thread_flag(TIF_NOTIFY_RESUME);
	force_iret();
}
EXPORT_SYMBOL_GPL(start_thread);

+1 −0
Original line number Diff line number Diff line
@@ -239,6 +239,7 @@ start_thread_common(struct pt_regs *regs, unsigned long new_ip,
	regs->cs		= _cs;
	regs->ss		= _ss;
	regs->flags		= X86_EFLAGS_IF;
	force_iret();
}

void
Loading