Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d05c5130 authored by Marcin Nowakowski's avatar Marcin Nowakowski Committed by Ralf Baechle
Browse files

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: default avatarMarcin Nowakowski <marcin.nowakowski@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/14336/


Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent e3031b32
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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) {
+2 −0
Original line number Diff line number Diff line
@@ -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) {
+6 −0
Original line number Diff line number Diff line
@@ -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  */