Skip to content

Commit

Permalink
ARM: 5715/1: Make kprobes unregistration SMP safe
Browse files Browse the repository at this point in the history
ARM kprobes use an illegal instruction to trigger kprobes. In the
current implementation, there's a race between the unregistration of a
kprobe and the illegal instruction exception handler if they run at the
same time on different cores.

When reading the value of the undefined instruction, the exception
handler might get the original legal instruction as just patched
concurrently by arch_disarm_kprobe(). When this happen the kprobe
handler won't run, and thus the exception handler will oops because it
believe it just hit an undefined instruction in kernel space.

The following patch synchronizes the code patching in the kprobes
unregistration using stop_machine and thus avoids the above race.

Signed-off-by: Frederic RISS <frederic.riss@gmail.com>
Acked-by: Nicolas Pitre <nico@fluxnic.net>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Frederic Riss authored and Russell King committed Sep 21, 2009
1 parent df297bf commit 2003b7a
Showing 1 changed file with 17 additions and 2 deletions.
19 changes: 17 additions & 2 deletions arch/arm/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/stop_machine.h>
#include <linux/stringify.h>
#include <asm/traps.h>
#include <asm/cacheflush.h>
Expand Down Expand Up @@ -83,10 +84,24 @@ void __kprobes arch_arm_kprobe(struct kprobe *p)
flush_insns(p->addr, 1);
}

/*
* The actual disarming is done here on each CPU and synchronized using
* stop_machine. This synchronization is necessary on SMP to avoid removing
* a probe between the moment the 'Undefined Instruction' exception is raised
* and the moment the exception handler reads the faulting instruction from
* memory.
*/
int __kprobes __arch_disarm_kprobe(void *p)
{
struct kprobe *kp = p;
*kp->addr = kp->opcode;
flush_insns(kp->addr, 1);
return 0;
}

void __kprobes arch_disarm_kprobe(struct kprobe *p)
{
*p->addr = p->opcode;
flush_insns(p->addr, 1);
stop_machine(__arch_disarm_kprobe, p, &cpu_online_map);
}

void __kprobes arch_remove_kprobe(struct kprobe *p)
Expand Down

0 comments on commit 2003b7a

Please sign in to comment.