Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

alpha: fix FEN fault handling

Type 3 instruction fault (FPU insn with FPU disabled) is handled
by quietly enabling FPU and returning. Which is fine, except that
we need to do that both for fault in userland and in the kernel;
the latter *can* legitimately happen - all it takes is this:

.global _start
_start:
call_pal 0xae
lda $0, 0
ldq $0, 0($0)

- call_pal CLRFEN to clear "FPU enabled" flag and arrange for
a signal delivery (SIGSEGV in this case).

Fixed by moving the handling of type 3 into the common part of
do_entIF(), before we check for kernel vs. user mode.

Incidentally, check for kernel mode is unidiomatic; the normal
way to do that is !user_mode(regs). The difference is that
the open-coded variant treats any of bits 63..3 of regs->ps being
set as "it's user mode" while the normal approach is to check just
the bit 3. PS is a 4-bit register and regs->ps always will have
bits 63..4 clear, so the open-code variant here is actually equivalent
to !user_mode(regs). Harder to follow, though...

Reproducer above will crash any box where CLRFEN is not ignored by
PAL (== any actual hardware, AFAICS; PAL used in qemu doesn't
bother implementing that crap).

Cc: stable@vger.kernel.org # all way back...
Reviewed-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Matt Turner <mattst88@gmail.com>

authored by

Al Viro and committed by
Matt Turner
d3c51b70 4da2bd30

+15 -15
+15 -15
arch/alpha/kernel/traps.c
··· 233 233 { 234 234 int signo, code; 235 235 236 - if ((regs->ps & ~IPL_MAX) == 0) { 236 + if (type == 3) { /* FEN fault */ 237 + /* Irritating users can call PAL_clrfen to disable the 238 + FPU for the process. The kernel will then trap in 239 + do_switch_stack and undo_switch_stack when we try 240 + to save and restore the FP registers. 241 + 242 + Given that GCC by default generates code that uses the 243 + FP registers, PAL_clrfen is not useful except for DoS 244 + attacks. So turn the bleeding FPU back on and be done 245 + with it. */ 246 + current_thread_info()->pcb.flags |= 1; 247 + __reload_thread(&current_thread_info()->pcb); 248 + return; 249 + } 250 + if (!user_mode(regs)) { 237 251 if (type == 1) { 238 252 const unsigned int *data 239 253 = (const unsigned int *) regs->pc; ··· 379 365 } 380 366 } 381 367 break; 382 - 383 - case 3: /* FEN fault */ 384 - /* Irritating users can call PAL_clrfen to disable the 385 - FPU for the process. The kernel will then trap in 386 - do_switch_stack and undo_switch_stack when we try 387 - to save and restore the FP registers. 388 - 389 - Given that GCC by default generates code that uses the 390 - FP registers, PAL_clrfen is not useful except for DoS 391 - attacks. So turn the bleeding FPU back on and be done 392 - with it. */ 393 - current_thread_info()->pcb.flags |= 1; 394 - __reload_thread(&current_thread_info()->pcb); 395 - return; 396 368 397 369 case 5: /* illoc */ 398 370 default: /* unexpected instruction-fault type */