Commit 5c02ece8 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar
Browse files

x86/kprobes: Fix ordering while text-patching

Kprobes does something like:

          /* guarantees nothing, INT3 will become visible at some point, maybe */

	  /* guarantees the bytes after INT3 are unused */
	  /* implies IPI-sync, kprobe really is enabled */

	    text_poke_bp(INT3 + tail);
	    /* implies IPI-sync, so tail is guaranteed visible */
	    /* guarantees nothing, old will maybe become visible */



Now the problem is that on register, the synchronize_rcu_tasks() does
not imply sufficient to guarantee all CPUs have already observed INT3
(although in practice this is exceedingly unlikely not to have
happened) (similar to how MEMBARRIER_CMD_PRIVATE_EXPEDITED does not

Worse, even if it did, we'd have to do 2 synchronize calls to provide
the guarantee we're looking for, the first to ensure INT3 is visible,
the second to guarantee nobody is then still using the instruction
bytes after INT3.

Similar on unregister; the synchronize_rcu() between
__unregister_kprobe_top() and __unregister_kprobe_bottom() does not
guarantee all CPUs are free of the INT3 (and observe the old text).

Therefore, sprinkle some IPI-sync love around. This guarantees that
all CPUs agree on the text and RCU once again provides the required

Tested-by: default avatarAlexei Starovoitov <>
Tested-by: default avatarSteven Rostedt (VMware) <>
Signed-off-by: default avatarPeter Zijlstra (Intel) <>
Reviewed-by: default avatarMathieu Desnoyers <>
Acked-by: default avatarMasami Hiramatsu <>
Acked-by: default avatarAlexei Starovoitov <>
Acked-by: default avatarPaul E. McKenney <>
Cc: Andy Lutomirski <>
Cc: Borislav Petkov <>
Cc: Brian Gerst <>
Cc: Denys Vlasenko <>
Cc: H. Peter Anvin <>
Cc: Linus Torvalds <>
Cc: Peter Zijlstra <>
Cc: Thomas Gleixner <>

Signed-off-by: default avatarIngo Molnar <>
parent ab09e95c
......@@ -42,6 +42,7 @@ extern void text_poke_early(void *addr, const void *opcode, size_t len);
* an inconsistent instruction while you patch.
extern void *text_poke(void *addr, const void *opcode, size_t len);
extern void text_poke_sync(void);
extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len);
extern int poke_int3_handler(struct pt_regs *regs);
extern void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate);
......@@ -936,6 +936,11 @@ static void do_sync_core(void *info)
void text_poke_sync(void)
on_each_cpu(do_sync_core, NULL, 1);
struct text_poke_loc {
s32 rel_addr; /* addr := _stext + rel_addr */
s32 rel32;
......@@ -1085,7 +1090,7 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries
for (i = 0; i < nr_entries; i++)
text_poke(text_poke_addr(&tp[i]), &int3, sizeof(int3));
on_each_cpu(do_sync_core, NULL, 1);
* Second step: update all but the first byte of the patched range.
......@@ -1107,7 +1112,7 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries
* not necessary and we'd be safe even without it. But
* better safe than sorry (plus there's not only Intel).
on_each_cpu(do_sync_core, NULL, 1);
......@@ -1123,7 +1128,7 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries
if (do_sync)
on_each_cpu(do_sync_core, NULL, 1);
* sync_core() implies an smp_mb() and orders this store against
......@@ -502,11 +502,13 @@ int arch_prepare_kprobe(struct kprobe *p)
void arch_arm_kprobe(struct kprobe *p)
text_poke(p->addr, ((unsigned char []){INT3_INSN_OPCODE}), 1);
void arch_disarm_kprobe(struct kprobe *p)
text_poke(p->addr, &p->opcode, 1);
void arch_remove_kprobe(struct kprobe *p)
......@@ -444,14 +444,10 @@ void arch_optimize_kprobes(struct list_head *oplist)
/* Replace a relative jump with a breakpoint (int3). */
void arch_unoptimize_kprobe(struct optimized_kprobe *op)
u8 insn_buff[JMP32_INSN_SIZE];
/* Set int3 to first byte for kprobes */
insn_buff[0] = INT3_INSN_OPCODE;
memcpy(insn_buff + 1, op->optinsn.copied_insn, DISP32_SIZE);
text_poke_bp(op->kp.addr, insn_buff, JMP32_INSN_SIZE,
text_gen_insn(JMP32_INSN_OPCODE, op->kp.addr, op->optinsn.insn));
text_poke(op->kp.addr + INT3_INSN_SIZE,
op->optinsn.copied_insn, DISP32_SIZE);
Supports Markdown
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