MIPS: tracing: disable uprobe/kprobe on compact branch instructions

Current instruction decoder for uprobe/kprobe handler only handles
branches with delay slots. For compact branches the behaviour is rather
unpredictable - and depending on the encoding of a compact branch
instruction may result in one (or more) of:
- executing an instruction that follows a branch which wasn't in a delay
  slot and shouldn't have been executed
- incorrectly emulating a branch leading to a jump to a wrong location
- unexpected branching out of the single-stepped code and never reaching
  the breakpoint that should terminate the probe handler

Results of these actions are generally unpredictable, but can end up
with a probed application or kernel crash, so disable placing probes on
compact branches until they are handled properly.

Signed-off-by: Marcin Nowakowski <marcin.nowakowski@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/14336/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Marcin Nowakowski 2016-09-30 11:33:46 +02:00 committed by Ralf Baechle
parent e3031b3284
commit d05c513069
4 changed files with 48 additions and 0 deletions

View File

@ -866,3 +866,37 @@ int __compute_return_epc(struct pt_regs *regs)
force_sig(SIGBUS, current); force_sig(SIGBUS, current);
return -EFAULT; return -EFAULT;
} }
#if (defined CONFIG_KPROBES) || (defined CONFIG_UPROBES)
int __insn_is_compact_branch(union mips_instruction insn)
{
if (!cpu_has_mips_r6)
return 0;
switch (insn.i_format.opcode) {
case blezl_op:
case bgtzl_op:
case blez_op:
case bgtz_op:
/*
* blez[l] and bgtz[l] opcodes with non-zero rt
* are MIPS R6 compact branches
*/
if (insn.i_format.rt)
return 1;
break;
case bc6_op:
case balc6_op:
case pop10_op:
case pop30_op:
case pop66_op:
case pop76_op:
return 1;
}
return 0;
}
EXPORT_SYMBOL_GPL(__insn_is_compact_branch);
#endif /* CONFIG_KPROBES || CONFIG_UPROBES */

View File

@ -106,6 +106,12 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
goto out; goto out;
} }
if (__insn_is_compact_branch(insn)) {
pr_notice("Kprobes for compact branches are not supported\n");
ret = -EINVAL;
goto out;
}
/* insn: must be on special executable page on mips. */ /* insn: must be on special executable page on mips. */
p->ainsn.insn = get_insn_slot(); p->ainsn.insn = get_insn_slot();
if (!p->ainsn.insn) { if (!p->ainsn.insn) {

View File

@ -13,6 +13,8 @@
#include <asm/inst.h> #include <asm/inst.h>
int __insn_is_compact_branch(union mips_instruction insn);
static inline int __insn_has_delay_slot(const union mips_instruction insn) static inline int __insn_has_delay_slot(const union mips_instruction insn)
{ {
switch (insn.i_format.opcode) { switch (insn.i_format.opcode) {

View File

@ -36,6 +36,12 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *aup,
return -EINVAL; return -EINVAL;
inst.word = aup->insn[0]; inst.word = aup->insn[0];
if (__insn_is_compact_branch(inst)) {
pr_notice("Uprobes for compact branches are not supported\n");
return -EINVAL;
}
aup->ixol[0] = aup->insn[insn_has_delay_slot(inst)]; aup->ixol[0] = aup->insn[insn_has_delay_slot(inst)];
aup->ixol[1] = UPROBE_BRK_UPROBE_XOL; /* NOP */ aup->ixol[1] = UPROBE_BRK_UPROBE_XOL; /* NOP */