Kernel: pass FPU restore exception to user process
Previously, user processes could cause a kernel panic upon FPU state restore, by passing bogus FPU state to the kernel (through e.g. sigreturn). With this patch, the process is now sent a SIGFPE signal instead.
This commit is contained in:
@@ -322,18 +322,24 @@ PUBLIC void save_fpu(struct proc *pr)
|
||||
#endif
|
||||
}
|
||||
|
||||
PUBLIC void restore_fpu(struct proc *pr)
|
||||
PUBLIC int restore_fpu(struct proc *pr)
|
||||
{
|
||||
int failed;
|
||||
|
||||
if(!proc_used_fpu(pr)) {
|
||||
fninit();
|
||||
pr->p_misc_flags |= MF_FPU_INITIALIZED;
|
||||
} else {
|
||||
if(osfxsr_feature) {
|
||||
fxrstor(pr->p_fpu_state.fpu_save_area_p);
|
||||
failed = fxrstor(pr->p_fpu_state.fpu_save_area_p);
|
||||
} else {
|
||||
frstor(pr->p_fpu_state.fpu_save_area_p);
|
||||
failed = frstor(pr->p_fpu_state.fpu_save_area_p);
|
||||
}
|
||||
|
||||
if (failed) return EINVAL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
PUBLIC void cpu_identify(void)
|
||||
|
||||
@@ -224,6 +224,17 @@ PUBLIC void exception_handler(int is_nested, struct exception_frame * frame)
|
||||
panic("Copy involving a user pointer failed unexpectedly!");
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass any error resulting from restoring FPU state, as a FPU
|
||||
* exception to the process.
|
||||
*/
|
||||
if (((void*)frame->eip >= (void*)fxrstor &&
|
||||
(void *)frame->eip <= (void*)__fxrstor_end) ||
|
||||
((void*)frame->eip >= (void*)frstor &&
|
||||
(void *)frame->eip <= (void*)__frstor_end)) {
|
||||
frame->eip = (reg_t) __frstor_failure;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(frame->vector == PAGE_FAULT_VECTOR) {
|
||||
|
||||
@@ -99,8 +99,11 @@ _PROTOTYPE( void fninit, (void));
|
||||
_PROTOTYPE( void clts, (void));
|
||||
_PROTOTYPE( void fxsave, (void *));
|
||||
_PROTOTYPE( void fnsave, (void *));
|
||||
_PROTOTYPE( void fxrstor, (void *));
|
||||
_PROTOTYPE( void frstor, (void *));
|
||||
_PROTOTYPE( int fxrstor, (void *));
|
||||
_PROTOTYPE( int __fxrstor_end, (void *));
|
||||
_PROTOTYPE( int frstor, (void *));
|
||||
_PROTOTYPE( int __frstor_end, (void *));
|
||||
_PROTOTYPE( int __frstor_failure, (void *));
|
||||
_PROTOTYPE( unsigned short fnstsw, (void));
|
||||
_PROTOTYPE( void fnstcw, (unsigned short* cw));
|
||||
|
||||
|
||||
@@ -608,21 +608,29 @@ ENTRY(fnsave)
|
||||
ret
|
||||
|
||||
/*===========================================================================*/
|
||||
/* fxrstor */
|
||||
/* fxrstor */
|
||||
/*===========================================================================*/
|
||||
ENTRY(fxrstor)
|
||||
mov 4(%esp), %eax
|
||||
fxrstor (%eax) /* Do not change the operand! (gas2ack) */
|
||||
fxrstor (%eax)
|
||||
ENTRY(__fxrstor_end)
|
||||
xor %eax, %eax
|
||||
ret
|
||||
|
||||
/*===========================================================================*/
|
||||
/* frstor */
|
||||
/* frstor */
|
||||
/*===========================================================================*/
|
||||
ENTRY(frstor)
|
||||
mov 4(%esp), %eax
|
||||
frstor (%eax) /* Do not change the operand! (gas2ack) */
|
||||
frstor (%eax)
|
||||
ENTRY(__frstor_end)
|
||||
xor %eax, %eax
|
||||
ret
|
||||
|
||||
/* Shared exception handler for both fxrstor and frstor. */
|
||||
ENTRY(__frstor_failure)
|
||||
mov $1, %eax
|
||||
ret
|
||||
|
||||
/*===========================================================================*/
|
||||
/* read_cr0 */
|
||||
|
||||
@@ -548,7 +548,9 @@ LABEL(copr_not_available)
|
||||
push %ebp
|
||||
mov $0, %ebp
|
||||
call _C_LABEL(context_stop)
|
||||
jmp _C_LABEL(copr_not_available_handler)
|
||||
call _C_LABEL(copr_not_available_handler)
|
||||
/* reached upon failure only */
|
||||
jmp _C_LABEL(switch_to_user)
|
||||
|
||||
copr_not_available_in_kernel:
|
||||
pushl $0
|
||||
|
||||
@@ -1885,7 +1885,15 @@ PUBLIC void copr_not_available_handler(void)
|
||||
* restore the current process' state and let it run again, do not
|
||||
* schedule!
|
||||
*/
|
||||
restore_fpu(p);
|
||||
if (restore_fpu(p) != OK) {
|
||||
/* Restoring FPU state failed. This is always the process's own
|
||||
* fault. Send a signal, and schedule another process instead.
|
||||
*/
|
||||
*local_fpu_owner = NULL;
|
||||
cause_sig(proc_nr(p), SIGFPE);
|
||||
return;
|
||||
}
|
||||
|
||||
*local_fpu_owner = p;
|
||||
context_stop(proc_addr(KERNEL));
|
||||
restore_user_context(p);
|
||||
|
||||
@@ -32,7 +32,7 @@ _PROTOTYPE( void cycles_accounting_init, (void) );
|
||||
_PROTOTYPE( void context_stop, (struct proc * p) );
|
||||
/* this is a wrapper to make calling it from assembly easier */
|
||||
_PROTOTYPE( void context_stop_idle, (void) );
|
||||
_PROTOTYPE( void restore_fpu, (struct proc *) );
|
||||
_PROTOTYPE( int restore_fpu, (struct proc *) );
|
||||
_PROTOTYPE( void save_fpu, (struct proc *) );
|
||||
_PROTOTYPE( void save_local_fpu, (struct proc *) );
|
||||
_PROTOTYPE( void fpu_sigcontext, (struct proc *, struct sigframe *fr, struct sigcontext *sc) );
|
||||
|
||||
Reference in New Issue
Block a user