Skip to content

Commit 5c02ece

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
x86/kprobes: Fix ordering while text-patching
Kprobes does something like: register: arch_arm_kprobe() text_poke(INT3) /* guarantees nothing, INT3 will become visible at some point, maybe */ kprobe_optimizer() /* guarantees the bytes after INT3 are unused */ synchronize_rcu_tasks(); text_poke_bp(JMP32); /* implies IPI-sync, kprobe really is enabled */ unregister: __disarm_kprobe() unoptimize_kprobe() text_poke_bp(INT3 + tail); /* implies IPI-sync, so tail is guaranteed visible */ arch_disarm_kprobe() text_poke(old); /* guarantees nothing, old will maybe become visible */ synchronize_rcu() free-stuff 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 imply MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE). 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 guaranteed. Tested-by: Alexei Starovoitov <[email protected]> Tested-by: Steven Rostedt (VMware) <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Mathieu Desnoyers <[email protected]> Acked-by: Masami Hiramatsu <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Acked-by: Paul E. McKenney <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Brian Gerst <[email protected]> Cc: Denys Vlasenko <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent ab09e95 commit 5c02ece

File tree

4 files changed

+15
-11
lines changed

4 files changed

+15
-11
lines changed

arch/x86/include/asm/text-patching.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ extern void text_poke_early(void *addr, const void *opcode, size_t len);
4242
* an inconsistent instruction while you patch.
4343
*/
4444
extern void *text_poke(void *addr, const void *opcode, size_t len);
45+
extern void text_poke_sync(void);
4546
extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len);
4647
extern int poke_int3_handler(struct pt_regs *regs);
4748
extern void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate);

arch/x86/kernel/alternative.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,11 @@ static void do_sync_core(void *info)
936936
sync_core();
937937
}
938938

939+
void text_poke_sync(void)
940+
{
941+
on_each_cpu(do_sync_core, NULL, 1);
942+
}
943+
939944
struct text_poke_loc {
940945
s32 rel_addr; /* addr := _stext + rel_addr */
941946
s32 rel32;
@@ -1085,7 +1090,7 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries
10851090
for (i = 0; i < nr_entries; i++)
10861091
text_poke(text_poke_addr(&tp[i]), &int3, sizeof(int3));
10871092

1088-
on_each_cpu(do_sync_core, NULL, 1);
1093+
text_poke_sync();
10891094

10901095
/*
10911096
* 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
11071112
* not necessary and we'd be safe even without it. But
11081113
* better safe than sorry (plus there's not only Intel).
11091114
*/
1110-
on_each_cpu(do_sync_core, NULL, 1);
1115+
text_poke_sync();
11111116
}
11121117

11131118
/*
@@ -1123,7 +1128,7 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries
11231128
}
11241129

11251130
if (do_sync)
1126-
on_each_cpu(do_sync_core, NULL, 1);
1131+
text_poke_sync();
11271132

11281133
/*
11291134
* sync_core() implies an smp_mb() and orders this store against

arch/x86/kernel/kprobes/core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,11 +502,13 @@ int arch_prepare_kprobe(struct kprobe *p)
502502
void arch_arm_kprobe(struct kprobe *p)
503503
{
504504
text_poke(p->addr, ((unsigned char []){INT3_INSN_OPCODE}), 1);
505+
text_poke_sync();
505506
}
506507

507508
void arch_disarm_kprobe(struct kprobe *p)
508509
{
509510
text_poke(p->addr, &p->opcode, 1);
511+
text_poke_sync();
510512
}
511513

512514
void arch_remove_kprobe(struct kprobe *p)

arch/x86/kernel/kprobes/opt.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -444,14 +444,10 @@ void arch_optimize_kprobes(struct list_head *oplist)
444444
/* Replace a relative jump with a breakpoint (int3). */
445445
void arch_unoptimize_kprobe(struct optimized_kprobe *op)
446446
{
447-
u8 insn_buff[JMP32_INSN_SIZE];
448-
449-
/* Set int3 to first byte for kprobes */
450-
insn_buff[0] = INT3_INSN_OPCODE;
451-
memcpy(insn_buff + 1, op->optinsn.copied_insn, DISP32_SIZE);
452-
453-
text_poke_bp(op->kp.addr, insn_buff, JMP32_INSN_SIZE,
454-
text_gen_insn(JMP32_INSN_OPCODE, op->kp.addr, op->optinsn.insn));
447+
arch_arm_kprobe(&op->kp);
448+
text_poke(op->kp.addr + INT3_INSN_SIZE,
449+
op->optinsn.copied_insn, DISP32_SIZE);
450+
text_poke_sync();
455451
}
456452

457453
/*

0 commit comments

Comments
 (0)