Commit d1895183 authored by Andi Kleen's avatar Andi Kleen Committed by Andi Kleen

[PATCH] x86: Fix i386 and x86_64 fault information pollution

a userspace fault or a kernelspace fault which will result in the
immediate death of the process.  They should not be filled in as a
result of a kernelspace fault which can be fixed up.

Otherwise, if the process is handling SIGSEGV and examining the fault
information, this can result in the kernel space fault trashing the
previously stored fault information if it arrives between the
userspace fault happening and the SIGSEGV being delivered to the process.
Signed-off-by: default avatarJeff Dike <jdike@addtoit.com>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Acked-by: default avatarJan Beulich <jbeulich@novell.com>
--
 arch/i386/kernel/traps.c   |   24 ++++++++++++++++++------
 arch/x86_64/kernel/traps.c |   30 +++++++++++++++++++++++-------
 2 files changed, 41 insertions(+), 13 deletions(-)
parent 00e065ea
...@@ -476,8 +476,6 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, int vm86, ...@@ -476,8 +476,6 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, int vm86,
siginfo_t *info) siginfo_t *info)
{ {
struct task_struct *tsk = current; struct task_struct *tsk = current;
tsk->thread.error_code = error_code;
tsk->thread.trap_no = trapnr;
if (regs->eflags & VM_MASK) { if (regs->eflags & VM_MASK) {
if (vm86) if (vm86)
...@@ -489,6 +487,18 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, int vm86, ...@@ -489,6 +487,18 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, int vm86,
goto kernel_trap; goto kernel_trap;
trap_signal: { trap_signal: {
/*
* We want error_code and trap_no set for userspace faults and
* kernelspace faults which result in die(), but not
* kernelspace faults which are fixed up. die() gives the
* process no chance to handle the signal and notice the
* kernel fault information, so that won't result in polluting
* the information about previously queued, but not yet
* delivered, faults. See also do_general_protection below.
*/
tsk->thread.error_code = error_code;
tsk->thread.trap_no = trapnr;
if (info) if (info)
force_sig_info(signr, info, tsk); force_sig_info(signr, info, tsk);
else else
...@@ -497,8 +507,11 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, int vm86, ...@@ -497,8 +507,11 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, int vm86,
} }
kernel_trap: { kernel_trap: {
if (!fixup_exception(regs)) if (!fixup_exception(regs)) {
tsk->thread.error_code = error_code;
tsk->thread.trap_no = trapnr;
die(str, regs, error_code); die(str, regs, error_code);
}
return; return;
} }
...@@ -603,9 +616,6 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs, ...@@ -603,9 +616,6 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs,
} }
put_cpu(); put_cpu();
current->thread.error_code = error_code;
current->thread.trap_no = 13;
if (regs->eflags & VM_MASK) if (regs->eflags & VM_MASK)
goto gp_in_vm86; goto gp_in_vm86;
...@@ -624,6 +634,8 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs, ...@@ -624,6 +634,8 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs,
gp_in_kernel: gp_in_kernel:
if (!fixup_exception(regs)) { if (!fixup_exception(regs)) {
current->thread.error_code = error_code;
current->thread.trap_no = 13;
if (notify_die(DIE_GPF, "general protection fault", regs, if (notify_die(DIE_GPF, "general protection fault", regs,
error_code, 13, SIGSEGV) == NOTIFY_STOP) error_code, 13, SIGSEGV) == NOTIFY_STOP)
return; return;
......
...@@ -581,10 +581,20 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, ...@@ -581,10 +581,20 @@ static void __kprobes do_trap(int trapnr, int signr, char *str,
{ {
struct task_struct *tsk = current; struct task_struct *tsk = current;
if (user_mode(regs)) {
/*
* We want error_code and trap_no set for userspace
* faults and kernelspace faults which result in
* die(), but not kernelspace faults which are fixed
* up. die() gives the process no chance to handle
* the signal and notice the kernel fault information,
* so that won't result in polluting the information
* about previously queued, but not yet delivered,
* faults. See also do_general_protection below.
*/
tsk->thread.error_code = error_code; tsk->thread.error_code = error_code;
tsk->thread.trap_no = trapnr; tsk->thread.trap_no = trapnr;
if (user_mode(regs)) {
if (exception_trace && unhandled_signal(tsk, signr)) if (exception_trace && unhandled_signal(tsk, signr))
printk(KERN_INFO printk(KERN_INFO
"%s[%d] trap %s rip:%lx rsp:%lx error:%lx\n", "%s[%d] trap %s rip:%lx rsp:%lx error:%lx\n",
...@@ -605,8 +615,11 @@ static void __kprobes do_trap(int trapnr, int signr, char *str, ...@@ -605,8 +615,11 @@ static void __kprobes do_trap(int trapnr, int signr, char *str,
fixup = search_exception_tables(regs->rip); fixup = search_exception_tables(regs->rip);
if (fixup) if (fixup)
regs->rip = fixup->fixup; regs->rip = fixup->fixup;
else else {
tsk->thread.error_code = error_code;
tsk->thread.trap_no = trapnr;
die(str, regs, error_code); die(str, regs, error_code);
}
return; return;
} }
} }
...@@ -682,10 +695,10 @@ asmlinkage void __kprobes do_general_protection(struct pt_regs * regs, ...@@ -682,10 +695,10 @@ asmlinkage void __kprobes do_general_protection(struct pt_regs * regs,
conditional_sti(regs); conditional_sti(regs);
if (user_mode(regs)) {
tsk->thread.error_code = error_code; tsk->thread.error_code = error_code;
tsk->thread.trap_no = 13; tsk->thread.trap_no = 13;
if (user_mode(regs)) {
if (exception_trace && unhandled_signal(tsk, SIGSEGV)) if (exception_trace && unhandled_signal(tsk, SIGSEGV))
printk(KERN_INFO printk(KERN_INFO
"%s[%d] general protection rip:%lx rsp:%lx error:%lx\n", "%s[%d] general protection rip:%lx rsp:%lx error:%lx\n",
...@@ -704,6 +717,9 @@ asmlinkage void __kprobes do_general_protection(struct pt_regs * regs, ...@@ -704,6 +717,9 @@ asmlinkage void __kprobes do_general_protection(struct pt_regs * regs,
regs->rip = fixup->fixup; regs->rip = fixup->fixup;
return; return;
} }
tsk->thread.error_code = error_code;
tsk->thread.trap_no = 13;
if (notify_die(DIE_GPF, "general protection fault", regs, if (notify_die(DIE_GPF, "general protection fault", regs,
error_code, 13, SIGSEGV) == NOTIFY_STOP) error_code, 13, SIGSEGV) == NOTIFY_STOP)
return; return;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment