Skip to content

Commit

Permalink
x86/alternatives: Add int3_emulate_call() selftest
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
Peter Zijlstra authored and Ingo Molnar committed Jun 25, 2019
1 parent faeedb0 commit 7457c0d
Showing 1 changed file with 77 additions and 4 deletions.
81 changes: 77 additions & 4 deletions arch/x86/kernel/alternative.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,11 +615,83 @@ extern struct paravirt_patch_site __start_parainstructions[],
__stop_parainstructions[];
#endif /* CONFIG_PARAVIRT */

/*
* Self-test for the INT3 based CALL emulation code.
*
* This exercises int3_emulate_call() to make sure INT3 pt_regs are set up
* properly and that there is a stack gap between the INT3 frame and the
* previous context. Without this gap doing a virtual PUSH on the interrupted
* stack would corrupt the INT3 IRET frame.
*
* See entry_{32,64}.S for more details.
*/
static void __init int3_magic(unsigned int *ptr)
{
*ptr = 1;
}

extern __initdata unsigned long int3_selftest_ip; /* defined in asm below */

static int __init
int3_exception_notify(struct notifier_block *self, unsigned long val, void *data)
{
struct die_args *args = data;
struct pt_regs *regs = args->regs;

if (!regs || user_mode(regs))
return NOTIFY_DONE;

if (val != DIE_INT3)
return NOTIFY_DONE;

if (regs->ip - INT3_INSN_SIZE != int3_selftest_ip)
return NOTIFY_DONE;

int3_emulate_call(regs, (unsigned long)&int3_magic);
return NOTIFY_STOP;
}

static void __init int3_selftest(void)
{
static __initdata struct notifier_block int3_exception_nb = {
.notifier_call = int3_exception_notify,
.priority = INT_MAX-1, /* last */
};
unsigned int val = 0;

BUG_ON(register_die_notifier(&int3_exception_nb));

/*
* Basically: int3_magic(&val); but really complicated :-)
*
* Stick the address of the INT3 instruction into int3_selftest_ip,
* then trigger the INT3, padded with NOPs to match a CALL instruction
* length.
*/
asm volatile ("1: int3; nop; nop; nop; nop\n\t"
".pushsection .init.data,\"aw\"\n\t"
".align " __ASM_SEL(4, 8) "\n\t"
".type int3_selftest_ip, @object\n\t"
".size int3_selftest_ip, " __ASM_SEL(4, 8) "\n\t"
"int3_selftest_ip:\n\t"
__ASM_SEL(.long, .quad) " 1b\n\t"
".popsection\n\t"
: : __ASM_SEL_RAW(a, D) (&val) : "memory");

BUG_ON(val != 1);

unregister_die_notifier(&int3_exception_nb);
}

void __init alternative_instructions(void)
{
/* The patching is not fully atomic, so try to avoid local interruptions
that might execute the to be patched code.
Other CPUs are not running. */
int3_selftest();

/*
* The patching is not fully atomic, so try to avoid local
* interruptions that might execute the to be patched code.
* Other CPUs are not running.
*/
stop_nmi();

/*
Expand All @@ -644,10 +716,11 @@ void __init alternative_instructions(void)
_text, _etext);
}

if (!uniproc_patched || num_possible_cpus() == 1)
if (!uniproc_patched || num_possible_cpus() == 1) {
free_init_pages("SMP alternatives",
(unsigned long)__smp_locks,
(unsigned long)__smp_locks_end);
}
#endif

apply_paravirt(__parainstructions, __parainstructions_end);
Expand Down

0 comments on commit 7457c0d

Please sign in to comment.