xref: /kvm-unit-tests/x86/cet.c (revision 7bf8144ef1760f7979d12856cce25777b9129094)
179e53994SYang Weijiang 
279e53994SYang Weijiang #include "libcflat.h"
379e53994SYang Weijiang #include "x86/desc.h"
479e53994SYang Weijiang #include "x86/processor.h"
579e53994SYang Weijiang #include "x86/vm.h"
679e53994SYang Weijiang #include "x86/msr.h"
779e53994SYang Weijiang #include "vmalloc.h"
879e53994SYang Weijiang #include "alloc_page.h"
979e53994SYang Weijiang #include "fault_test.h"
1079e53994SYang Weijiang 
1179e53994SYang Weijiang 
1279e53994SYang Weijiang static unsigned char user_stack[0x400];
1379e53994SYang Weijiang static unsigned long rbx, rsi, rdi, rsp, rbp, r8, r9,
1479e53994SYang Weijiang 		     r10, r11, r12, r13, r14, r15;
1579e53994SYang Weijiang 
1679e53994SYang Weijiang static unsigned long expected_rip;
1779e53994SYang Weijiang static int cp_count;
1879e53994SYang Weijiang typedef u64 (*cet_test_func)(void);
1979e53994SYang Weijiang 
2079e53994SYang Weijiang cet_test_func func;
2179e53994SYang Weijiang 
2279e53994SYang Weijiang static u64 cet_shstk_func(void)
2379e53994SYang Weijiang {
2479e53994SYang Weijiang 	unsigned long *ret_addr, *ssp;
2579e53994SYang Weijiang 
2679e53994SYang Weijiang 	/* rdsspq %rax */
2779e53994SYang Weijiang 	asm volatile (".byte 0xf3, 0x48, 0x0f, 0x1e, 0xc8" : "=a"(ssp));
2879e53994SYang Weijiang 
2979e53994SYang Weijiang 	asm("movq %%rbp,%0" : "=r"(ret_addr));
3079e53994SYang Weijiang 	printf("The return-address in shadow-stack = 0x%lx, in normal stack = 0x%lx\n",
3179e53994SYang Weijiang 	       *ssp, *(ret_addr + 1));
3279e53994SYang Weijiang 
3379e53994SYang Weijiang 	/*
3479e53994SYang Weijiang 	 * In below line, it modifies the return address, it'll trigger #CP
3579e53994SYang Weijiang 	 * while function is returning. The error-code is 0x1, meaning it's
3679e53994SYang Weijiang 	 * caused by a near RET instruction, and the execution is terminated
3779e53994SYang Weijiang 	 * when HW detects the violation.
3879e53994SYang Weijiang 	 */
3979e53994SYang Weijiang 	printf("Try to temper the return-address, this causes #CP on returning...\n");
4079e53994SYang Weijiang 	*(ret_addr + 1) = 0xdeaddead;
4179e53994SYang Weijiang 
4279e53994SYang Weijiang 	return 0;
4379e53994SYang Weijiang }
4479e53994SYang Weijiang 
4579e53994SYang Weijiang static u64 cet_ibt_func(void)
4679e53994SYang Weijiang {
4779e53994SYang Weijiang 	/*
4879e53994SYang Weijiang 	 * In below assembly code, the first instruction at lable 2 is not
4979e53994SYang Weijiang 	 * endbr64, it'll trigger #CP with error code 0x3, and the execution
5079e53994SYang Weijiang 	 * is terminated when HW detects the violation.
5179e53994SYang Weijiang 	 */
5279e53994SYang Weijiang 	printf("No endbr64 instruction at jmp target, this triggers #CP...\n");
5379e53994SYang Weijiang 	asm volatile ("movq $2, %rcx\n"
5479e53994SYang Weijiang 		      "dec %rcx\n"
55*7bf8144eSZixuan Wang 		      "leaq 2f(%rip), %rax\n"
5679e53994SYang Weijiang 		      "jmp *%rax \n"
5779e53994SYang Weijiang 		      "2:\n"
5879e53994SYang Weijiang 		      "dec %rcx\n");
5979e53994SYang Weijiang 	return 0;
6079e53994SYang Weijiang }
6179e53994SYang Weijiang 
6279e53994SYang Weijiang void test_func(void);
6379e53994SYang Weijiang void test_func(void) {
6479e53994SYang Weijiang 	asm volatile (
6579e53994SYang Weijiang 			/* IRET into user mode */
6679e53994SYang Weijiang 			"pushq %[user_ds]\n\t"
6779e53994SYang Weijiang 			"pushq %[user_stack_top]\n\t"
6879e53994SYang Weijiang 			"pushfq\n\t"
6979e53994SYang Weijiang 			"pushq %[user_cs]\n\t"
70*7bf8144eSZixuan Wang 			"lea user_mode(%%rip), %%rax\n\t"
71*7bf8144eSZixuan Wang 			"pushq %%rax\n\t"
7279e53994SYang Weijiang 			"iretq\n"
7379e53994SYang Weijiang 
7479e53994SYang Weijiang 			"user_mode:\n\t"
7579e53994SYang Weijiang 			"call *%[func]\n\t"
7679e53994SYang Weijiang 			::
7779e53994SYang Weijiang 			[func]"m"(func),
7879e53994SYang Weijiang 			[user_ds]"i"(USER_DS),
7979e53994SYang Weijiang 			[user_cs]"i"(USER_CS),
8079e53994SYang Weijiang 			[user_stack_top]"r"(user_stack +
81*7bf8144eSZixuan Wang 					sizeof(user_stack))
82*7bf8144eSZixuan Wang 			: "rax");
8379e53994SYang Weijiang }
8479e53994SYang Weijiang 
8579e53994SYang Weijiang #define SAVE_REGS() \
8679e53994SYang Weijiang 	asm ("movq %%rbx, %0\t\n"  \
8779e53994SYang Weijiang 	     "movq %%rsi, %1\t\n"  \
8879e53994SYang Weijiang 	     "movq %%rdi, %2\t\n"  \
8979e53994SYang Weijiang 	     "movq %%rsp, %3\t\n"  \
9079e53994SYang Weijiang 	     "movq %%rbp, %4\t\n"  \
9179e53994SYang Weijiang 	     "movq %%r8, %5\t\n"   \
9279e53994SYang Weijiang 	     "movq %%r9, %6\t\n"   \
9379e53994SYang Weijiang 	     "movq %%r10, %7\t\n"  \
9479e53994SYang Weijiang 	     "movq %%r11, %8\t\n"  \
9579e53994SYang Weijiang 	     "movq %%r12, %9\t\n"  \
9679e53994SYang Weijiang 	     "movq %%r13, %10\t\n" \
9779e53994SYang Weijiang 	     "movq %%r14, %11\t\n" \
9879e53994SYang Weijiang 	     "movq %%r15, %12\t\n" :: \
9979e53994SYang Weijiang 	     "m"(rbx), "m"(rsi), "m"(rdi), "m"(rsp), "m"(rbp), \
10079e53994SYang Weijiang 	     "m"(r8), "m"(r9), "m"(r10),  "m"(r11), "m"(r12),  \
10179e53994SYang Weijiang 	     "m"(r13), "m"(r14), "m"(r15));
10279e53994SYang Weijiang 
10379e53994SYang Weijiang #define RESTOR_REGS() \
10479e53994SYang Weijiang 	asm ("movq %0, %%rbx\t\n"  \
10579e53994SYang Weijiang 	     "movq %1, %%rsi\t\n"  \
10679e53994SYang Weijiang 	     "movq %2, %%rdi\t\n"  \
10779e53994SYang Weijiang 	     "movq %3, %%rsp\t\n"  \
10879e53994SYang Weijiang 	     "movq %4, %%rbp\t\n"  \
10979e53994SYang Weijiang 	     "movq %5, %%r8\t\n"   \
11079e53994SYang Weijiang 	     "movq %6, %%r9\t\n"   \
11179e53994SYang Weijiang 	     "movq %7, %%r10\t\n"  \
11279e53994SYang Weijiang 	     "movq %8, %%r11\t\n"  \
11379e53994SYang Weijiang 	     "movq %9, %%r12\t\n"  \
11479e53994SYang Weijiang 	     "movq %10, %%r13\t\n" \
11579e53994SYang Weijiang 	     "movq %11, %%r14\t\n" \
11679e53994SYang Weijiang 	     "movq %12, %%r15\t\n" ::\
11779e53994SYang Weijiang 	     "m"(rbx), "m"(rsi), "m"(rdi), "m"(rsp), "m"(rbp), \
11879e53994SYang Weijiang 	     "m"(r8), "m"(r9), "m"(r10), "m"(r11), "m"(r12),   \
11979e53994SYang Weijiang 	     "m"(r13), "m"(r14), "m"(r15));
12079e53994SYang Weijiang 
12179e53994SYang Weijiang #define RUN_TEST() \
12279e53994SYang Weijiang 	do {		\
12379e53994SYang Weijiang 		SAVE_REGS();    \
12479e53994SYang Weijiang 		asm volatile ("pushq %%rax\t\n"           \
12579e53994SYang Weijiang 			      "leaq 1f(%%rip), %%rax\t\n" \
12679e53994SYang Weijiang 			      "movq %%rax, %0\t\n"        \
12779e53994SYang Weijiang 			      "popq %%rax\t\n"            \
12879e53994SYang Weijiang 			      "call test_func\t\n"         \
12979e53994SYang Weijiang 			      "1:" ::"m"(expected_rip) : "rax", "rdi"); \
13079e53994SYang Weijiang 		RESTOR_REGS(); \
13179e53994SYang Weijiang 	} while (0)
13279e53994SYang Weijiang 
13379e53994SYang Weijiang #define ENABLE_SHSTK_BIT 0x1
13479e53994SYang Weijiang #define ENABLE_IBT_BIT   0x4
13579e53994SYang Weijiang 
13679e53994SYang Weijiang static void handle_cp(struct ex_regs *regs)
13779e53994SYang Weijiang {
13879e53994SYang Weijiang 	cp_count++;
13979e53994SYang Weijiang 	printf("In #CP exception handler, error_code = 0x%lx\n",
14079e53994SYang Weijiang 		regs->error_code);
14179e53994SYang Weijiang 	asm("jmp *%0" :: "m"(expected_rip));
14279e53994SYang Weijiang }
14379e53994SYang Weijiang 
14479e53994SYang Weijiang int main(int ac, char **av)
14579e53994SYang Weijiang {
14679e53994SYang Weijiang 	char *shstk_virt;
14779e53994SYang Weijiang 	unsigned long shstk_phys;
14879e53994SYang Weijiang 	unsigned long *ptep;
14979e53994SYang Weijiang 	pteval_t pte = 0;
15079e53994SYang Weijiang 
15179e53994SYang Weijiang 	cp_count = 0;
15279e53994SYang Weijiang 	if (!this_cpu_has(X86_FEATURE_SHSTK)) {
15379e53994SYang Weijiang 		printf("SHSTK not enabled\n");
15479e53994SYang Weijiang 		return report_summary();
15579e53994SYang Weijiang 	}
15679e53994SYang Weijiang 
15779e53994SYang Weijiang 	if (!this_cpu_has(X86_FEATURE_IBT)) {
15879e53994SYang Weijiang 		printf("IBT not enabled\n");
15979e53994SYang Weijiang 		return report_summary();
16079e53994SYang Weijiang 	}
16179e53994SYang Weijiang 
16279e53994SYang Weijiang 	setup_vm();
16379e53994SYang Weijiang 	setup_idt();
16479e53994SYang Weijiang 	handle_exception(21, handle_cp);
16579e53994SYang Weijiang 
16679e53994SYang Weijiang 	/* Allocate one page for shadow-stack. */
16779e53994SYang Weijiang 	shstk_virt = alloc_vpage();
16879e53994SYang Weijiang 	shstk_phys = (unsigned long)virt_to_phys(alloc_page());
16979e53994SYang Weijiang 
17079e53994SYang Weijiang 	/* Install the new page. */
17179e53994SYang Weijiang 	pte = shstk_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK;
17279e53994SYang Weijiang 	install_pte(current_page_table(), 1, shstk_virt, pte, 0);
17379e53994SYang Weijiang 	memset(shstk_virt, 0x0, PAGE_SIZE);
17479e53994SYang Weijiang 
17579e53994SYang Weijiang 	/* Mark it as shadow-stack page. */
17679e53994SYang Weijiang 	ptep = get_pte_level(current_page_table(), shstk_virt, 1);
17779e53994SYang Weijiang 	*ptep &= ~PT_WRITABLE_MASK;
17879e53994SYang Weijiang 	*ptep |= PT_DIRTY_MASK;
17979e53994SYang Weijiang 
18079e53994SYang Weijiang 	/* Flush the paging cache. */
18179e53994SYang Weijiang 	invlpg((void *)shstk_phys);
18279e53994SYang Weijiang 
18379e53994SYang Weijiang 	/* Enable shadow-stack protection */
18479e53994SYang Weijiang 	wrmsr(MSR_IA32_U_CET, ENABLE_SHSTK_BIT);
18579e53994SYang Weijiang 
18679e53994SYang Weijiang 	/* Store shadow-stack pointer. */
18779e53994SYang Weijiang 	wrmsr(MSR_IA32_PL3_SSP, (u64)(shstk_virt + 0x1000));
18879e53994SYang Weijiang 
18979e53994SYang Weijiang 	/* Enable CET master control bit in CR4. */
19079e53994SYang Weijiang 	write_cr4(read_cr4() | X86_CR4_CET);
19179e53994SYang Weijiang 
19279e53994SYang Weijiang 	func = cet_shstk_func;
19379e53994SYang Weijiang 	RUN_TEST();
19479e53994SYang Weijiang 	report(cp_count == 1, "Completed shadow-stack protection test successfully.");
19579e53994SYang Weijiang 	cp_count = 0;
19679e53994SYang Weijiang 
19779e53994SYang Weijiang 	/* Do user-mode indirect-branch-tracking test.*/
19879e53994SYang Weijiang 	func = cet_ibt_func;
19979e53994SYang Weijiang 	/* Enable indirect-branch tracking */
20079e53994SYang Weijiang 	wrmsr(MSR_IA32_U_CET, ENABLE_IBT_BIT);
20179e53994SYang Weijiang 
20279e53994SYang Weijiang 	RUN_TEST();
20379e53994SYang Weijiang 	report(cp_count == 1, "Completed Indirect-branch tracking test successfully.");
20479e53994SYang Weijiang 
20579e53994SYang Weijiang 	write_cr4(read_cr4() & ~X86_CR4_CET);
20679e53994SYang Weijiang 	wrmsr(MSR_IA32_U_CET, 0);
20779e53994SYang Weijiang 
20879e53994SYang Weijiang 	return report_summary();
20979e53994SYang Weijiang }
210