diff options
author | David Vrabel <david.vrabel@citrix.com> | 2012-10-19 12:29:07 -0400 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2012-10-19 15:17:59 -0400 |
commit | a349e23d1cf746f8bdc603dcc61fae9ee4a695f6 (patch) | |
tree | 49df313d4d1e37189e1ae6f052d11381e1443f80 /arch | |
parent | ee7b5958e2494619ee3ff52de68580feed6906a2 (diff) |
xen/x86: don't corrupt %eip when returning from a signal handler
In 32 bit guests, if a userspace process has %eax == -ERESTARTSYS
(-512) or -ERESTARTNOINTR (-513) when it is interrupted by an event
/and/ the process has a pending signal then %eip (and %eax) are
corrupted when returning to the main process after handling the
signal. The application may then crash with SIGSEGV or a SIGILL or it
may have subtly incorrect behaviour (depending on what instruction it
returned to).
The occurs because handle_signal() is incorrectly thinking that there
is a system call that needs to restarted so it adjusts %eip and %eax
to re-execute the system call instruction (even though user space had
not done a system call).
If %eax == -514 (-ERESTARTNOHAND (-514) or -ERESTART_RESTARTBLOCK
(-516) then handle_signal() only corrupted %eax (by setting it to
-EINTR). This may cause the application to crash or have incorrect
behaviour.
handle_signal() assumes that regs->orig_ax >= 0 means a system call so
any kernel entry point that is not for a system call must push a
negative value for orig_ax. For example, for physical interrupts on
bare metal the inverse of the vector is pushed and page_fault() sets
regs->orig_ax to -1, overwriting the hardware provided error code.
xen_hypervisor_callback() was incorrectly pushing 0 for orig_ax
instead of -1.
Classic Xen kernels pushed %eax which works as %eax cannot be both
non-negative and -RESTARTSYS (etc.), but using -1 is consistent with
other non-system call entry points and avoids some of the tests in
handle_signal().
There were similar bugs in xen_failsafe_callback() of both 32 and
64-bit guests. If the fault was corrected and the normal return path
was used then 0 was incorrectly pushed as the value for orig_ax.
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Acked-by: Jan Beulich <JBeulich@suse.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
Cc: stable@vger.kernel.org
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/entry_32.S | 8 | ||||
-rw-r--r-- | arch/x86/kernel/entry_64.S | 2 |
2 files changed, 6 insertions, 4 deletions
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 623f28837476..8f8e8eea9085 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S | |||
@@ -1016,7 +1016,7 @@ ENTRY(xen_sysenter_target) | |||
1016 | 1016 | ||
1017 | ENTRY(xen_hypervisor_callback) | 1017 | ENTRY(xen_hypervisor_callback) |
1018 | CFI_STARTPROC | 1018 | CFI_STARTPROC |
1019 | pushl_cfi $0 | 1019 | pushl_cfi $-1 /* orig_ax = -1 => not a system call */ |
1020 | SAVE_ALL | 1020 | SAVE_ALL |
1021 | TRACE_IRQS_OFF | 1021 | TRACE_IRQS_OFF |
1022 | 1022 | ||
@@ -1058,14 +1058,16 @@ ENTRY(xen_failsafe_callback) | |||
1058 | 2: mov 8(%esp),%es | 1058 | 2: mov 8(%esp),%es |
1059 | 3: mov 12(%esp),%fs | 1059 | 3: mov 12(%esp),%fs |
1060 | 4: mov 16(%esp),%gs | 1060 | 4: mov 16(%esp),%gs |
1061 | /* EAX == 0 => Category 1 (Bad segment) | ||
1062 | EAX != 0 => Category 2 (Bad IRET) */ | ||
1061 | testl %eax,%eax | 1063 | testl %eax,%eax |
1062 | popl_cfi %eax | 1064 | popl_cfi %eax |
1063 | lea 16(%esp),%esp | 1065 | lea 16(%esp),%esp |
1064 | CFI_ADJUST_CFA_OFFSET -16 | 1066 | CFI_ADJUST_CFA_OFFSET -16 |
1065 | jz 5f | 1067 | jz 5f |
1066 | addl $16,%esp | 1068 | addl $16,%esp |
1067 | jmp iret_exc # EAX != 0 => Category 2 (Bad IRET) | 1069 | jmp iret_exc |
1068 | 5: pushl_cfi $0 # EAX == 0 => Category 1 (Bad segment) | 1070 | 5: pushl_cfi $-1 /* orig_ax = -1 => not a system call */ |
1069 | SAVE_ALL | 1071 | SAVE_ALL |
1070 | jmp ret_from_exception | 1072 | jmp ret_from_exception |
1071 | CFI_ENDPROC | 1073 | CFI_ENDPROC |
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 69babd8c834f..dcdd0ea33a32 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
@@ -1363,7 +1363,7 @@ ENTRY(xen_failsafe_callback) | |||
1363 | CFI_RESTORE r11 | 1363 | CFI_RESTORE r11 |
1364 | addq $0x30,%rsp | 1364 | addq $0x30,%rsp |
1365 | CFI_ADJUST_CFA_OFFSET -0x30 | 1365 | CFI_ADJUST_CFA_OFFSET -0x30 |
1366 | pushq_cfi $0 | 1366 | pushq_cfi $-1 /* orig_ax = -1 => not a system call */ |
1367 | SAVE_ALL | 1367 | SAVE_ALL |
1368 | jmp error_exit | 1368 | jmp error_exit |
1369 | CFI_ENDPROC | 1369 | CFI_ENDPROC |