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

MIPS: math-emu: Prevent wrong ISA mode instruction emulation

Terminate FPU emulation immediately whenever an ISA mode switch has been
observed. This is so that we do not interpret machine code in the wrong
mode, for example when a regular MIPS FPU instruction has been placed in
a delay slot of a jump that switches into the MIPS16 mode, as with the
following code (taken from a GCC test suite case):

00400650 <set_fast_math>:
400650: 3c020100 lui v0,0x100
400654: 03e00008 jr ra
400658: 44c2f800 ctc1 v0,c1_fcsr
40065c: 00000000 nop

[...]

004012d0 <__libc_csu_init>:
4012d0: f000 6a02 li v0,2
4012d4: f150 0b1c la v1,3f9430 <_DYNAMIC-0x6df0>
4012d8: f400 3240 sll v0,16
4012dc: e269 addu v0,v1
4012de: 659a move gp,v0
4012e0: f00c 64f6 save a0-a2,48,ra,s0-s1
4012e4: 673c move s1,gp
4012e6: f010 9978 lw v1,-32744(s1)
4012ea: d204 sw v0,16(sp)
4012ec: eb40 jalr v1
4012ee: 653b move t9,v1
4012f0: f010 997c lw v1,-32740(s1)
4012f4: f030 9920 lw s1,-32736(s1)
4012f8: e32f subu v1,s1
4012fa: 326b sra v0,v1,2
4012fc: d206 sw v0,24(sp)
4012fe: 220c beqz v0,401318 <__libc_csu_init+0x48>
401300: 6800 li s0,0
401302: 99e0 lw a3,0(s1)
401304: 4801 addiu s0,1
401306: 960e lw a2,56(sp)
401308: 4904 addiu s1,4
40130a: 950d lw a1,52(sp)
40130c: 940c lw a0,48(sp)
40130e: ef40 jalr a3
401310: 653f move t9,a3
401312: 9206 lw v0,24(sp)
401314: ea0a cmp v0,s0
401316: 61f5 btnez 401302 <__libc_csu_init+0x32>
401318: 6476 restore 48,ra,s0-s1
40131a: e8a0 jrc ra

Here `set_fast_math' is called from `40130e' (`40130f' with the ISA bit)
and emulation triggers for the CTC1 instruction. As it is in a jump
delay slot emulation continues from `401312' (`401313' with the ISA
bit). However we have no path to handle MIPS16 FPU code emulation,
because there are no MIPS16 FPU instructions. So the default emulation
path is taken, interpreting a 32-bit word fetched by `get_user' from
`401313' as a regular MIPS instruction, which is:

401313: f5ea0a92 sdc1 $f10,2706(t7)

This makes the FPU emulator proceed with the supposed SDC1 instruction
and consequently makes the program considered here terminate with
SIGSEGV.

A similar although less severe issue exists with pure-microMIPS
processors in the case where similarly an FPU instruction is emulated in
a delay slot of a register jump that (incorrectly) switches into the
regular MIPS mode. A subsequent instruction fetch from the jump's
target is supposed to cause an Address Error exception, however instead
we proceed with regular MIPS FPU emulation.

For simplicity then, always terminate the emulation loop whenever a mode
change is detected, denoted by an ISA mode bit flip. As from commit
377cb1b6c16a ("MIPS: Disable MIPS16/microMIPS crap for platforms not
supporting these ASEs.") the result of `get_isa16_mode' can be hardcoded
to 0, so we need to examine the ISA mode bit by hand.

This complements commit 102cedc32a6e ("MIPS: microMIPS: Floating point
support.") which added JALX decoding to FPU emulation.

Fixes: 102cedc32a6e ("MIPS: microMIPS: Floating point support.")
Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
Cc: James Hogan <james.hogan@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: stable@vger.kernel.org # 3.9+
Patchwork: https://patchwork.linux-mips.org/patch/16393/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Maciej W. Rozycki and committed by
Ralf Baechle
13769eba 0b17c967

+38
+38
arch/mips/math-emu/cp1emu.c
··· 2524 2524 return 0; 2525 2525 } 2526 2526 2527 + /* 2528 + * Emulate FPU instructions. 2529 + * 2530 + * If we use FPU hardware, then we have been typically called to handle 2531 + * an unimplemented operation, such as where an operand is a NaN or 2532 + * denormalized. In that case exit the emulation loop after a single 2533 + * iteration so as to let hardware execute any subsequent instructions. 2534 + * 2535 + * If we have no FPU hardware or it has been disabled, then continue 2536 + * emulating floating-point instructions until one of these conditions 2537 + * has occurred: 2538 + * 2539 + * - a non-FPU instruction has been encountered, 2540 + * 2541 + * - an attempt to emulate has ended with a signal, 2542 + * 2543 + * - the ISA mode has been switched. 2544 + * 2545 + * We need to terminate the emulation loop if we got switched to the 2546 + * MIPS16 mode, whether supported or not, so that we do not attempt 2547 + * to emulate a MIPS16 instruction as a regular MIPS FPU instruction. 2548 + * Similarly if we got switched to the microMIPS mode and only the 2549 + * regular MIPS mode is supported, so that we do not attempt to emulate 2550 + * a microMIPS instruction as a regular MIPS FPU instruction. Or if 2551 + * we got switched to the regular MIPS mode and only the microMIPS mode 2552 + * is supported, so that we do not attempt to emulate a regular MIPS 2553 + * instruction that should cause an Address Error exception instead. 2554 + * For simplicity we always terminate upon an ISA mode switch. 2555 + */ 2527 2556 int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 2528 2557 int has_fpu, void *__user *fault_addr) 2529 2558 { ··· 2637 2608 if (has_fpu) 2638 2609 break; 2639 2610 if (sig) 2611 + break; 2612 + /* 2613 + * We have to check for the ISA bit explicitly here, 2614 + * because `get_isa16_mode' may return 0 if support 2615 + * for code compression has been globally disabled, 2616 + * or otherwise we may produce the wrong signal or 2617 + * even proceed successfully where we must not. 2618 + */ 2619 + if ((xcp->cp0_epc ^ prevepc) & 0x1) 2640 2620 break; 2641 2621 2642 2622 cond_resched();