Commit 527fda69 authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Linus Torvalds

[PATCH] parisc: fix security hole

The default interruption handler "handle_interruption" does not properly
check to see if the faulting space is the same as the users space.
The problem lies in the fact that if a fault happens on the gateway we
will not deliver signals to the process, the process will not die, and we
may continue handling the same fault in a loop forever.  Any malicious
user code can crash the kernel by jumping into the gateway page at an
inopportune address.

The solution is to check if the user has the right privilege and if the
spaces match, both faulting and currently active. The best action is to
force the process back to it's own space of execution at address zero, and
let it take a SIGSEGV. The process can never recover from this because it
happens immediately after the return from the interrupt via rfi or rfir.
Committed-by: default avatarCarlos O'Donell <carlos@parisc-linux.org>
parent f922d99b
...@@ -379,9 +379,9 @@ void transfer_pim_to_trap_frame(struct pt_regs *regs) ...@@ -379,9 +379,9 @@ void transfer_pim_to_trap_frame(struct pt_regs *regs)
/* /*
* This routine handles page faults. It determines the address, * This routine is called as a last resort when everything else
* and the problem, and then passes it off to one of the appropriate * has gone clearly wrong. We get called for faults in kernel space,
* routines. * and HPMC's.
*/ */
void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long offset) void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long offset)
{ {
...@@ -432,9 +432,16 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o ...@@ -432,9 +432,16 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o
* system will shut down immediately right here. */ * system will shut down immediately right here. */
pdc_soft_power_button(0); pdc_soft_power_button(0);
/* Gutter the processor! */ /* Call kernel panic() so reboot timeouts work properly
for(;;) * FIXME: This function should be on the list of
; * panic notifiers, and we should call panic
* directly from the location that we wish.
* e.g. We should not call panic from
* parisc_terminate, but rather the oter way around.
* This hack works, prints the panic message twice,
* and it enables reboot timers!
*/
panic(msg);
} }
void handle_interruption(int code, struct pt_regs *regs) void handle_interruption(int code, struct pt_regs *regs)
...@@ -448,6 +455,36 @@ void handle_interruption(int code, struct pt_regs *regs) ...@@ -448,6 +455,36 @@ void handle_interruption(int code, struct pt_regs *regs)
else else
local_irq_enable(); local_irq_enable();
/* Security check:
* If the priority level is still user, and the
* faulting space is not equal to the active space
* then the user is attempting something in a space
* that does not belong to them. Kill the process.
*
* This is normally the situation when the user
* attempts to jump into the kernel space at the
* wrong offset, be it at the gateway page or a
* random location.
*
* We cannot normally signal the process because it
* could *be* on the gateway page, and processes
* executing on the gateway page can't have signals
* delivered.
*
* We merely readjust the address into the users
* space, at a destination address of zero, and
* allow processing to continue.
*/
if (((unsigned long)regs->iaoq[0] & 3) &&
((unsigned long)regs->iasq[0] != (unsigned long)regs->sr[7])) {
/* Kill the user process later */
regs->iaoq[0] = 0 | 3;
regs->iaoq[1] = regs->iaoq[0] + 4;
regs->iasq[0] = regs->iasq[0] = regs->sr[7];
regs->gr[0] &= ~PSW_B;
return;
}
#if 0 #if 0
printk(KERN_CRIT "Interruption # %d\n", code); printk(KERN_CRIT "Interruption # %d\n", code);
#endif #endif
...@@ -472,7 +509,7 @@ void handle_interruption(int code, struct pt_regs *regs) ...@@ -472,7 +509,7 @@ void handle_interruption(int code, struct pt_regs *regs)
case 3: case 3:
/* Recovery counter trap */ /* Recovery counter trap */
regs->gr[0] &= ~PSW_R; regs->gr[0] &= ~PSW_R;
if (regs->iasq[0]) if (user_space(regs))
handle_gdb_break(regs, TRAP_TRACE); handle_gdb_break(regs, TRAP_TRACE);
/* else this must be the start of a syscall - just let it run */ /* else this must be the start of a syscall - just let it run */
return; return;
...@@ -553,12 +590,12 @@ void handle_interruption(int code, struct pt_regs *regs) ...@@ -553,12 +590,12 @@ void handle_interruption(int code, struct pt_regs *regs)
/* Set to zero, and let the userspace app figure it out from /* Set to zero, and let the userspace app figure it out from
the insn pointed to by si_addr */ the insn pointed to by si_addr */
si.si_code = 0; si.si_code = 0;
si.si_addr = (void *) regs->iaoq[0]; si.si_addr = (void __user *) regs->iaoq[0];
force_sig_info(SIGFPE, &si, current); force_sig_info(SIGFPE, &si, current);
return; return;
} else }
/* The kernel doesn't want to handle condition codes */ /* The kernel doesn't want to handle condition codes */
break; break;
case 14: case 14:
/* Assist Exception Trap, i.e. floating point exception. */ /* Assist Exception Trap, i.e. floating point exception. */
...@@ -576,9 +613,16 @@ void handle_interruption(int code, struct pt_regs *regs) ...@@ -576,9 +613,16 @@ void handle_interruption(int code, struct pt_regs *regs)
/* Fall through */ /* Fall through */
case 17: case 17:
/* Non-access data TLB miss fault/Non-access data page fault */ /* Non-access data TLB miss fault/Non-access data page fault */
/* TODO: Still need to add slow path emulation code here */ /* FIXME:
/* TODO: Understand what is meant by the TODO listed Still need to add slow path emulation code here!
above this one. (Carlos) */ If the insn used a non-shadow register, then the tlb
handlers could not have their side-effect (e.g. probe
writing to a target register) emulated since rfir would
erase the changes to said register. Instead we have to
setup everything, call this function we are in, and emulate
by hand. Technically we need to emulate:
fdc,fdce,pdc,"fic,4f",prober,probeir,probew, probeiw
*/
fault_address = regs->ior; fault_address = regs->ior;
fault_space = regs->isr; fault_space = regs->isr;
break; break;
...@@ -609,7 +653,7 @@ void handle_interruption(int code, struct pt_regs *regs) ...@@ -609,7 +653,7 @@ void handle_interruption(int code, struct pt_regs *regs)
case 25: case 25:
/* Taken branch trap */ /* Taken branch trap */
regs->gr[0] &= ~PSW_T; regs->gr[0] &= ~PSW_T;
if (regs->iasq[0]) if (user_space(regs))
handle_gdb_break(regs, TRAP_BRANCH); handle_gdb_break(regs, TRAP_BRANCH);
/* else this must be the start of a syscall - just let it /* else this must be the start of a syscall - just let it
* run. * run.
......
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