Skip to content

Commit

Permalink
powerpc/kprobes: Skip livepatch_handler() for jprobes
Browse files Browse the repository at this point in the history
ftrace_caller() depends on a modified regs->nip to detect if a certain
function has been livepatched. However, with KPROBES_ON_FTRACE, it is
possible for regs->nip to have been modified by the kprobes pre_handler
(jprobes, for instance). In this case, we do not want to invoke the
livepatch_handler so as not to consume the livepatch stack.

To distinguish between the two (kprobes and livepatch), we check if
there is an active kprobe on the current function. If there is, then we
know for sure that it must have modified the NIP as we don't support
livepatching a kprobe'd function. In this case, we simply skip the
livepatch_handler and branch to the new NIP. Otherwise, the
livepatch_handler is invoked.

Fixes: ead514d ("powerpc/kprobes: Add support for KPROBES_ON_FTRACE")
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Reviewed-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
  • Loading branch information
rnav authored and mpe committed Jun 16, 2017
1 parent a4979a7 commit c05b8c4
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 5 deletions.
1 change: 1 addition & 0 deletions arch/powerpc/include/asm/kprobes.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ extern int kprobe_exceptions_notify(struct notifier_block *self,
extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
extern int kprobe_handler(struct pt_regs *regs);
extern int kprobe_post_handler(struct pt_regs *regs);
extern int is_current_kprobe_addr(unsigned long addr);
#ifdef CONFIG_KPROBES_ON_FTRACE
extern int skip_singlestep(struct kprobe *p, struct pt_regs *regs,
struct kprobe_ctlblk *kcb);
Expand Down
6 changes: 6 additions & 0 deletions arch/powerpc/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);

struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};

int is_current_kprobe_addr(unsigned long addr)
{
struct kprobe *p = kprobe_running();
return (p && (unsigned long)p->addr == addr) ? 1 : 0;
}

bool arch_within_kprobe_blacklist(unsigned long addr)
{
return (addr >= (unsigned long)__kprobes_text_start &&
Expand Down
39 changes: 34 additions & 5 deletions arch/powerpc/kernel/trace/ftrace_64_mprofile.S
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,39 @@ ftrace_call:
bl ftrace_stub
nop

/* Load ctr with the possibly modified NIP */
ld r3, _NIP(r1)
mtctr r3
/* Load the possibly modified NIP */
ld r15, _NIP(r1)

#ifdef CONFIG_LIVEPATCH
cmpd r14,r3 /* has NIP been altered? */
cmpd r14, r15 /* has NIP been altered? */
#endif

#if defined(CONFIG_LIVEPATCH) && defined(CONFIG_KPROBES_ON_FTRACE)
/* NIP has not been altered, skip over further checks */
beq 1f

/* Check if there is an active kprobe on us */
subi r3, r14, 4
bl is_current_kprobe_addr
nop

/*
* If r3 == 1, then this is a kprobe/jprobe.
* else, this is livepatched function.
*
* The conditional branch for livepatch_handler below will use the
* result of this comparison. For kprobe/jprobe, we just need to branch to
* the new NIP, not call livepatch_handler. The branch below is bne, so we
* want CR0[EQ] to be true if this is a kprobe/jprobe. Which means we want
* CR0[EQ] = (r3 == 1).
*/
cmpdi r3, 1
1:
#endif

/* Load CTR with the possibly modified NIP */
mtctr r15

/* Restore gprs */
REST_GPR(0,r1)
REST_10GPRS(2,r1)
Expand All @@ -123,7 +149,10 @@ ftrace_call:
addi r1, r1, SWITCH_FRAME_SIZE

#ifdef CONFIG_LIVEPATCH
/* Based on the cmpd above, if the NIP was altered handle livepatch */
/*
* Based on the cmpd or cmpdi above, if the NIP was altered and we're
* not on a kprobe/jprobe, then handle livepatch.
*/
bne- livepatch_handler
#endif

Expand Down

0 comments on commit c05b8c4

Please sign in to comment.