Skip to content

Commit fd4363f

Browse files
Jiri KosinaH. Peter Anvin
authored andcommitted
x86: Introduce int3 (breakpoint)-based instruction patching
Introduce a method for run-time instruction patching on a live SMP kernel based on int3 breakpoint, completely avoiding the need for stop_machine(). The way this is achieved: - add a int3 trap to the address that will be patched - sync cores - update all but the first byte of the patched range - sync cores - replace the first byte (int3) by the first byte of replacing opcode - sync cores According to http://lkml.indiana.edu/hypermail/linux/kernel/1001.1/01530.html synchronization after replacing "all but first" instructions should not be necessary (on Intel hardware), as the syncing after the subsequent patching of the first byte provides enough safety. But there's not only Intel HW out there, and we'd rather be on a safe side. If any CPU instruction execution would collide with the patching, it'd be trapped by the int3 breakpoint and redirected to the provided "handler" (which would typically mean just skipping over the patched region, acting as "nop" has been there, in case we are doing nop -> jump and jump -> nop transitions). Ftrace has been using this very technique since 08d636b ("ftrace/x86: Have arch x86_64 use breakpoints instead of stop machine") for ages already, and jump labels are another obvious potential user of this. Based on activities of Masami Hiramatsu <[email protected]> a few years ago. Reviewed-by: Steven Rostedt <[email protected]> Reviewed-by: Masami Hiramatsu <[email protected]> Signed-off-by: Jiri Kosina <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: H. Peter Anvin <[email protected]>
1 parent ad81f05 commit fd4363f

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

arch/x86/include/asm/alternative.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ struct text_poke_param {
233233
};
234234

235235
extern void *text_poke(void *addr, const void *opcode, size_t len);
236+
extern void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler);
236237
extern void *text_poke_smp(void *addr, const void *opcode, size_t len);
237238
extern void text_poke_smp_batch(struct text_poke_param *params, int n);
238239

arch/x86/kernel/alternative.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/memory.h>
1212
#include <linux/stop_machine.h>
1313
#include <linux/slab.h>
14+
#include <linux/kdebug.h>
1415
#include <asm/alternative.h>
1516
#include <asm/sections.h>
1617
#include <asm/pgtable.h>
@@ -596,6 +597,111 @@ void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
596597
return addr;
597598
}
598599

600+
static void do_sync_core(void *info)
601+
{
602+
sync_core();
603+
}
604+
605+
static bool bp_patching_in_progress;
606+
static void *bp_int3_handler, *bp_int3_addr;
607+
608+
static int int3_notify(struct notifier_block *self, unsigned long val, void *data)
609+
{
610+
struct die_args *args = data;
611+
612+
/* bp_patching_in_progress */
613+
smp_rmb();
614+
615+
if (likely(!bp_patching_in_progress))
616+
return NOTIFY_DONE;
617+
618+
/* we are not interested in non-int3 faults and ring > 0 faults */
619+
if (val != DIE_INT3 || !args->regs || user_mode_vm(args->regs)
620+
|| args->regs->ip != (unsigned long)bp_int3_addr)
621+
return NOTIFY_DONE;
622+
623+
/* set up the specified breakpoint handler */
624+
args->regs->ip = (unsigned long) bp_int3_handler;
625+
626+
return NOTIFY_STOP;
627+
}
628+
/**
629+
* text_poke_bp() -- update instructions on live kernel on SMP
630+
* @addr: address to patch
631+
* @opcode: opcode of new instruction
632+
* @len: length to copy
633+
* @handler: address to jump to when the temporary breakpoint is hit
634+
*
635+
* Modify multi-byte instruction by using int3 breakpoint on SMP.
636+
* In contrary to text_poke_smp(), we completely avoid stop_machine() here,
637+
* and achieve the synchronization using int3 breakpoint.
638+
*
639+
* The way it is done:
640+
* - add a int3 trap to the address that will be patched
641+
* - sync cores
642+
* - update all but the first byte of the patched range
643+
* - sync cores
644+
* - replace the first byte (int3) by the first byte of
645+
* replacing opcode
646+
* - sync cores
647+
*
648+
* Note: must be called under text_mutex.
649+
*/
650+
void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler)
651+
{
652+
unsigned char int3 = 0xcc;
653+
654+
bp_int3_handler = handler;
655+
bp_int3_addr = (u8 *)addr + sizeof(int3);
656+
bp_patching_in_progress = true;
657+
/*
658+
* Corresponding read barrier in int3 notifier for
659+
* making sure the in_progress flags is correctly ordered wrt.
660+
* patching
661+
*/
662+
smp_wmb();
663+
664+
text_poke(addr, &int3, sizeof(int3));
665+
666+
on_each_cpu(do_sync_core, NULL, 1);
667+
668+
if (len - sizeof(int3) > 0) {
669+
/* patch all but the first byte */
670+
text_poke((char *)addr + sizeof(int3),
671+
(const char *) opcode + sizeof(int3),
672+
len - sizeof(int3));
673+
/*
674+
* According to Intel, this core syncing is very likely
675+
* not necessary and we'd be safe even without it. But
676+
* better safe than sorry (plus there's not only Intel).
677+
*/
678+
on_each_cpu(do_sync_core, NULL, 1);
679+
}
680+
681+
/* patch the first byte */
682+
text_poke(addr, opcode, sizeof(int3));
683+
684+
on_each_cpu(do_sync_core, NULL, 1);
685+
686+
bp_patching_in_progress = false;
687+
smp_wmb();
688+
689+
return addr;
690+
}
691+
692+
/* this one needs to run before anything else handles it as a
693+
* regular exception */
694+
static struct notifier_block int3_nb = {
695+
.priority = 0x7fffffff,
696+
.notifier_call = int3_notify
697+
};
698+
699+
static int __init int3_init(void)
700+
{
701+
return register_die_notifier(&int3_nb);
702+
}
703+
704+
arch_initcall(int3_init);
599705
/*
600706
* Cross-modifying kernel text with stop_machine().
601707
* This code originally comes from immediate value.

kernel/kprobes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1709,7 +1709,7 @@ EXPORT_SYMBOL_GPL(unregister_kprobes);
17091709

17101710
static struct notifier_block kprobe_exceptions_nb = {
17111711
.notifier_call = kprobe_exceptions_notify,
1712-
.priority = 0x7fffffff /* we need to be notified first */
1712+
.priority = 0x7ffffff0 /* High priority, but not first. */
17131713
};
17141714

17151715
unsigned long __weak arch_deref_entry_point(void *entry)

0 commit comments

Comments
 (0)