Skip to content

Commit

Permalink
MIPS: tracing: disable uprobe/kprobe on compact branch instructions
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Marcin Nowakowski authored and Ralf Baechle committed Oct 6, 2016
1 parent e3031b3 commit d05c513
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 0 deletions.
34 changes: 34 additions & 0 deletions arch/mips/kernel/branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -866,3 +866,37 @@ int __compute_return_epc(struct pt_regs *regs)
force_sig(SIGBUS, current);
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 */
6 changes: 6 additions & 0 deletions arch/mips/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
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. */
p->ainsn.insn = get_insn_slot();
if (!p->ainsn.insn) {
Expand Down
2 changes: 2 additions & 0 deletions arch/mips/kernel/probes-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#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)
{
switch (insn.i_format.opcode) {
Expand Down
6 changes: 6 additions & 0 deletions arch/mips/kernel/uprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *aup,
return -EINVAL;

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[1] = UPROBE_BRK_UPROBE_XOL; /* NOP */

Expand Down

0 comments on commit d05c513

Please sign in to comment.