Skip to content

Commit 7457c0d

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
x86/alternatives: Add int3_emulate_call() selftest
Given that the entry_*.S changes for this functionality are somewhat tricky, make sure the paths are tested every boot, instead of on the rare occasion when we trip an INT3 while rewriting text. Requested-by: Andy Lutomirski <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Josh Poimboeuf <[email protected]> Acked-by: Andy Lutomirski <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Signed-off-by: Ingo Molnar <[email protected]>
1 parent faeedb0 commit 7457c0d

File tree

1 file changed

+77
-4
lines changed

1 file changed

+77
-4
lines changed

arch/x86/kernel/alternative.c

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,11 +615,83 @@ extern struct paravirt_patch_site __start_parainstructions[],
615615
__stop_parainstructions[];
616616
#endif /* CONFIG_PARAVIRT */
617617

618+
/*
619+
* Self-test for the INT3 based CALL emulation code.
620+
*
621+
* This exercises int3_emulate_call() to make sure INT3 pt_regs are set up
622+
* properly and that there is a stack gap between the INT3 frame and the
623+
* previous context. Without this gap doing a virtual PUSH on the interrupted
624+
* stack would corrupt the INT3 IRET frame.
625+
*
626+
* See entry_{32,64}.S for more details.
627+
*/
628+
static void __init int3_magic(unsigned int *ptr)
629+
{
630+
*ptr = 1;
631+
}
632+
633+
extern __initdata unsigned long int3_selftest_ip; /* defined in asm below */
634+
635+
static int __init
636+
int3_exception_notify(struct notifier_block *self, unsigned long val, void *data)
637+
{
638+
struct die_args *args = data;
639+
struct pt_regs *regs = args->regs;
640+
641+
if (!regs || user_mode(regs))
642+
return NOTIFY_DONE;
643+
644+
if (val != DIE_INT3)
645+
return NOTIFY_DONE;
646+
647+
if (regs->ip - INT3_INSN_SIZE != int3_selftest_ip)
648+
return NOTIFY_DONE;
649+
650+
int3_emulate_call(regs, (unsigned long)&int3_magic);
651+
return NOTIFY_STOP;
652+
}
653+
654+
static void __init int3_selftest(void)
655+
{
656+
static __initdata struct notifier_block int3_exception_nb = {
657+
.notifier_call = int3_exception_notify,
658+
.priority = INT_MAX-1, /* last */
659+
};
660+
unsigned int val = 0;
661+
662+
BUG_ON(register_die_notifier(&int3_exception_nb));
663+
664+
/*
665+
* Basically: int3_magic(&val); but really complicated :-)
666+
*
667+
* Stick the address of the INT3 instruction into int3_selftest_ip,
668+
* then trigger the INT3, padded with NOPs to match a CALL instruction
669+
* length.
670+
*/
671+
asm volatile ("1: int3; nop; nop; nop; nop\n\t"
672+
".pushsection .init.data,\"aw\"\n\t"
673+
".align " __ASM_SEL(4, 8) "\n\t"
674+
".type int3_selftest_ip, @object\n\t"
675+
".size int3_selftest_ip, " __ASM_SEL(4, 8) "\n\t"
676+
"int3_selftest_ip:\n\t"
677+
__ASM_SEL(.long, .quad) " 1b\n\t"
678+
".popsection\n\t"
679+
: : __ASM_SEL_RAW(a, D) (&val) : "memory");
680+
681+
BUG_ON(val != 1);
682+
683+
unregister_die_notifier(&int3_exception_nb);
684+
}
685+
618686
void __init alternative_instructions(void)
619687
{
620-
/* The patching is not fully atomic, so try to avoid local interruptions
621-
that might execute the to be patched code.
622-
Other CPUs are not running. */
688+
int3_selftest();
689+
690+
/*
691+
* The patching is not fully atomic, so try to avoid local
692+
* interruptions that might execute the to be patched code.
693+
* Other CPUs are not running.
694+
*/
623695
stop_nmi();
624696

625697
/*
@@ -644,10 +716,11 @@ void __init alternative_instructions(void)
644716
_text, _etext);
645717
}
646718

647-
if (!uniproc_patched || num_possible_cpus() == 1)
719+
if (!uniproc_patched || num_possible_cpus() == 1) {
648720
free_init_pages("SMP alternatives",
649721
(unsigned long)__smp_locks,
650722
(unsigned long)__smp_locks_end);
723+
}
651724
#endif
652725

653726
apply_paravirt(__parainstructions, __parainstructions_end);

0 commit comments

Comments
 (0)