| From 29a2e2836ff9ea65a603c89df217f4198973a74f Mon Sep 17 00:00:00 2001 |
| From: Dmitry Adamushko <dmitry.adamushko@gmail.com> |
| Date: Thu, 22 Mar 2012 21:39:25 +0100 |
| Subject: x86-32: Fix endless loop when processing signals for kernel tasks |
| |
| From: Dmitry Adamushko <dmitry.adamushko@gmail.com> |
| |
| commit 29a2e2836ff9ea65a603c89df217f4198973a74f upstream. |
| |
| The problem occurs on !CONFIG_VM86 kernels [1] when a kernel-mode task |
| returns from a system call with a pending signal. |
| |
| A real-life scenario is a child of 'khelper' returning from a failed |
| kernel_execve() in ____call_usermodehelper() [ kernel/kmod.c ]. |
| kernel_execve() fails due to a pending SIGKILL, which is the result of |
| "kill -9 -1" (at least, busybox's init does it upon reboot). |
| |
| The loop is as follows: |
| |
| * syscall_exit_work: |
| - work_pending: // start_of_the_loop |
| - work_notify_sig: |
| - do_notify_resume() |
| - do_signal() |
| - if (!user_mode(regs)) return; |
| - resume_userspace // TIF_SIGPENDING is still set |
| - work_pending // so we call work_pending => goto |
| // start_of_the_loop |
| |
| More information can be found in another LKML thread: |
| http://www.serverphorums.com/read.php?12,457826 |
| |
| [1] the problem was also seen on MIPS. |
| |
| Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com> |
| Link: http://lkml.kernel.org/r/1332448765.2299.68.camel@dimm |
| Cc: Oleg Nesterov <oleg@redhat.com> |
| Cc: Roland McGrath <roland@hack.frob.com> |
| Cc: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: H. Peter Anvin <hpa@zytor.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/kernel/entry_32.S | 17 ++++++++++------- |
| 1 file changed, 10 insertions(+), 7 deletions(-) |
| |
| --- a/arch/x86/kernel/entry_32.S |
| +++ b/arch/x86/kernel/entry_32.S |
| @@ -99,12 +99,6 @@ |
| #endif |
| .endm |
| |
| -#ifdef CONFIG_VM86 |
| -#define resume_userspace_sig check_userspace |
| -#else |
| -#define resume_userspace_sig resume_userspace |
| -#endif |
| - |
| /* |
| * User gs save/restore |
| * |
| @@ -328,10 +322,19 @@ ret_from_exception: |
| preempt_stop(CLBR_ANY) |
| ret_from_intr: |
| GET_THREAD_INFO(%ebp) |
| -check_userspace: |
| +resume_userspace_sig: |
| +#ifdef CONFIG_VM86 |
| movl PT_EFLAGS(%esp), %eax # mix EFLAGS and CS |
| movb PT_CS(%esp), %al |
| andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax |
| +#else |
| + /* |
| + * We can be coming here from a syscall done in the kernel space, |
| + * e.g. a failed kernel_execve(). |
| + */ |
| + movl PT_CS(%esp), %eax |
| + andl $SEGMENT_RPL_MASK, %eax |
| +#endif |
| cmpl $USER_RPL, %eax |
| jb resume_kernel # not returning to v8086 or userspace |
| |