Commit eb3a7292 authored by Keshavamurthy Anil S's avatar Keshavamurthy Anil S Committed by Linus Torvalds

[PATCH] kprobes: fix race in recovery of reentrant probe

There is a window where a probe gets removed right after the probe is hit
on some different cpu.  In this case probe handlers can't find a matching
probe instance related to break address.  In this case we need to read the
original instruction at break address to see if that is not a break/int3
instruction and recover safely.

Previous code had a bug where we were not checking for the above race in
case of reentrant probes and the below patch fixes this race.

Tested on IA64, Powerpc, x86_64.
Signed-off-by: default avatarAnil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent df019b1d
...@@ -188,6 +188,19 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) ...@@ -188,6 +188,19 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
kcb->kprobe_status = KPROBE_REENTER; kcb->kprobe_status = KPROBE_REENTER;
return 1; return 1;
} else { } else {
if (regs->eflags & VM_MASK) {
/* We are in virtual-8086 mode. Return 0 */
goto no_kprobe;
}
if (*addr != BREAKPOINT_INSTRUCTION) {
/* The breakpoint instruction was removed by
* another cpu right after we hit, no further
* handling of this interrupt is appropriate
*/
regs->eip -= sizeof(kprobe_opcode_t);
ret = 1;
goto no_kprobe;
}
p = __get_cpu_var(current_kprobe); p = __get_cpu_var(current_kprobe);
if (p->break_handler && p->break_handler(p, regs)) { if (p->break_handler && p->break_handler(p, regs)) {
goto ss_probe; goto ss_probe;
......
...@@ -638,6 +638,13 @@ static int __kprobes pre_kprobes_handler(struct die_args *args) ...@@ -638,6 +638,13 @@ static int __kprobes pre_kprobes_handler(struct die_args *args)
if (p->break_handler && p->break_handler(p, regs)) { if (p->break_handler && p->break_handler(p, regs)) {
goto ss_probe; goto ss_probe;
} }
} else if (!is_ia64_break_inst(regs)) {
/* The breakpoint instruction was removed by
* another cpu right after we hit, no further
* handling of this interrupt is appropriate
*/
ret = 1;
goto no_kprobe;
} else { } else {
/* Not our break */ /* Not our break */
goto no_kprobe; goto no_kprobe;
......
...@@ -179,6 +179,18 @@ static inline int kprobe_handler(struct pt_regs *regs) ...@@ -179,6 +179,18 @@ static inline int kprobe_handler(struct pt_regs *regs)
kcb->kprobe_status = KPROBE_REENTER; kcb->kprobe_status = KPROBE_REENTER;
return 1; return 1;
} else { } else {
if (*addr != BREAKPOINT_INSTRUCTION) {
/* If trap variant, then it belongs not to us */
kprobe_opcode_t cur_insn = *addr;
if (is_trap(cur_insn))
goto no_kprobe;
/* The breakpoint instruction was removed by
* another cpu right after we hit, no further
* handling of this interrupt is appropriate
*/
ret = 1;
goto no_kprobe;
}
p = __get_cpu_var(current_kprobe); p = __get_cpu_var(current_kprobe);
if (p->break_handler && p->break_handler(p, regs)) { if (p->break_handler && p->break_handler(p, regs)) {
goto ss_probe; goto ss_probe;
......
...@@ -135,6 +135,14 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) ...@@ -135,6 +135,14 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
prepare_singlestep(p, regs, kcb); prepare_singlestep(p, regs, kcb);
return 1; return 1;
} else { } else {
if (*(u32 *)addr != BREAKPOINT_INSTRUCTION) {
/* The breakpoint instruction was removed by
* another cpu right after we hit, no further
* handling of this interrupt is appropriate
*/
ret = 1;
goto no_kprobe;
}
p = __get_cpu_var(current_kprobe); p = __get_cpu_var(current_kprobe);
if (p->break_handler && p->break_handler(p, regs)) if (p->break_handler && p->break_handler(p, regs))
goto ss_probe; goto ss_probe;
......
...@@ -334,6 +334,15 @@ int __kprobes kprobe_handler(struct pt_regs *regs) ...@@ -334,6 +334,15 @@ int __kprobes kprobe_handler(struct pt_regs *regs)
return 1; return 1;
} }
} else { } else {
if (*addr != BREAKPOINT_INSTRUCTION) {
/* The breakpoint instruction was removed by
* another cpu right after we hit, no further
* handling of this interrupt is appropriate
*/
regs->rip = (unsigned long)addr;
ret = 1;
goto no_kprobe;
}
p = __get_cpu_var(current_kprobe); p = __get_cpu_var(current_kprobe);
if (p->break_handler && p->break_handler(p, regs)) { if (p->break_handler && p->break_handler(p, regs)) {
goto ss_probe; goto ss_probe;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment