xref: /kvm-unit-tests/x86/cet.c (revision cd5f2fb4ad641c51fe0f1a85264dc3f6ede6e131)
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 static int cp_count;
1296add4abSYang Weijiang static unsigned long invalid_offset = 0xffffffffffffff;
1379e53994SYang Weijiang 
cet_shstk_func(void)1479e53994SYang Weijiang static u64 cet_shstk_func(void)
1579e53994SYang Weijiang {
1679e53994SYang Weijiang 	unsigned long *ret_addr, *ssp;
1779e53994SYang Weijiang 
1879e53994SYang Weijiang 	/* rdsspq %rax */
1979e53994SYang Weijiang 	asm volatile (".byte 0xf3, 0x48, 0x0f, 0x1e, 0xc8" : "=a"(ssp));
2079e53994SYang Weijiang 
2179e53994SYang Weijiang 	asm("movq %%rbp,%0" : "=r"(ret_addr));
2279e53994SYang Weijiang 	printf("The return-address in shadow-stack = 0x%lx, in normal stack = 0x%lx\n",
2379e53994SYang Weijiang 	       *ssp, *(ret_addr + 1));
2479e53994SYang Weijiang 
2579e53994SYang Weijiang 	/*
2679e53994SYang Weijiang 	 * In below line, it modifies the return address, it'll trigger #CP
2779e53994SYang Weijiang 	 * while function is returning. The error-code is 0x1, meaning it's
2879e53994SYang Weijiang 	 * caused by a near RET instruction, and the execution is terminated
2979e53994SYang Weijiang 	 * when HW detects the violation.
3079e53994SYang Weijiang 	 */
3179e53994SYang Weijiang 	printf("Try to temper the return-address, this causes #CP on returning...\n");
3279e53994SYang Weijiang 	*(ret_addr + 1) = 0xdeaddead;
3379e53994SYang Weijiang 
3479e53994SYang Weijiang 	return 0;
3579e53994SYang Weijiang }
3679e53994SYang Weijiang 
cet_ibt_func(void)3779e53994SYang Weijiang static u64 cet_ibt_func(void)
3879e53994SYang Weijiang {
3979e53994SYang Weijiang 	/*
40912c0d72SThomas Huth 	 * In below assembly code, the first instruction at label 2 is not
4179e53994SYang Weijiang 	 * endbr64, it'll trigger #CP with error code 0x3, and the execution
4279e53994SYang Weijiang 	 * is terminated when HW detects the violation.
4379e53994SYang Weijiang 	 */
4479e53994SYang Weijiang 	printf("No endbr64 instruction at jmp target, this triggers #CP...\n");
4579e53994SYang Weijiang 	asm volatile ("movq $2, %rcx\n"
4679e53994SYang Weijiang 		      "dec %rcx\n"
477bf8144eSZixuan Wang 		      "leaq 2f(%rip), %rax\n"
4879e53994SYang Weijiang 		      "jmp *%rax \n"
4979e53994SYang Weijiang 		      "2:\n"
5079e53994SYang Weijiang 		      "dec %rcx\n");
5179e53994SYang Weijiang 	return 0;
5279e53994SYang Weijiang }
5379e53994SYang Weijiang 
5479e53994SYang Weijiang #define ENABLE_SHSTK_BIT 0x1
5579e53994SYang Weijiang #define ENABLE_IBT_BIT   0x4
5679e53994SYang Weijiang 
handle_cp(struct ex_regs * regs)5779e53994SYang Weijiang static void handle_cp(struct ex_regs *regs)
5879e53994SYang Weijiang {
5979e53994SYang Weijiang 	cp_count++;
6079e53994SYang Weijiang 	printf("In #CP exception handler, error_code = 0x%lx\n",
6179e53994SYang Weijiang 		regs->error_code);
6296add4abSYang Weijiang 	/* Below jmp is expected to trigger #GP */
636a7a83edSPaolo Bonzini 	asm("jmpq *%0": :"m"(invalid_offset));
6479e53994SYang Weijiang }
6579e53994SYang Weijiang 
main(int ac,char ** av)6679e53994SYang Weijiang int main(int ac, char **av)
6779e53994SYang Weijiang {
6879e53994SYang Weijiang 	char *shstk_virt;
6979e53994SYang Weijiang 	unsigned long shstk_phys;
7079e53994SYang Weijiang 	unsigned long *ptep;
7179e53994SYang Weijiang 	pteval_t pte = 0;
7296add4abSYang Weijiang 	bool rvc;
7379e53994SYang Weijiang 
7479e53994SYang Weijiang 	cp_count = 0;
7579e53994SYang Weijiang 	if (!this_cpu_has(X86_FEATURE_SHSTK)) {
7679e53994SYang Weijiang 		printf("SHSTK not enabled\n");
7779e53994SYang Weijiang 		return report_summary();
7879e53994SYang Weijiang 	}
7979e53994SYang Weijiang 
8079e53994SYang Weijiang 	if (!this_cpu_has(X86_FEATURE_IBT)) {
8179e53994SYang Weijiang 		printf("IBT not enabled\n");
8279e53994SYang Weijiang 		return report_summary();
8379e53994SYang Weijiang 	}
8479e53994SYang Weijiang 
8579e53994SYang Weijiang 	setup_vm();
86*b069c221SMathias Krause 	handle_exception(CP_VECTOR, handle_cp);
8779e53994SYang Weijiang 
8879e53994SYang Weijiang 	/* Allocate one page for shadow-stack. */
8979e53994SYang Weijiang 	shstk_virt = alloc_vpage();
9079e53994SYang Weijiang 	shstk_phys = (unsigned long)virt_to_phys(alloc_page());
9179e53994SYang Weijiang 
9279e53994SYang Weijiang 	/* Install the new page. */
9379e53994SYang Weijiang 	pte = shstk_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK;
9479e53994SYang Weijiang 	install_pte(current_page_table(), 1, shstk_virt, pte, 0);
9579e53994SYang Weijiang 	memset(shstk_virt, 0x0, PAGE_SIZE);
9679e53994SYang Weijiang 
9779e53994SYang Weijiang 	/* Mark it as shadow-stack page. */
9879e53994SYang Weijiang 	ptep = get_pte_level(current_page_table(), shstk_virt, 1);
9979e53994SYang Weijiang 	*ptep &= ~PT_WRITABLE_MASK;
10079e53994SYang Weijiang 	*ptep |= PT_DIRTY_MASK;
10179e53994SYang Weijiang 
10279e53994SYang Weijiang 	/* Flush the paging cache. */
10379e53994SYang Weijiang 	invlpg((void *)shstk_phys);
10479e53994SYang Weijiang 
10579e53994SYang Weijiang 	/* Enable shadow-stack protection */
10679e53994SYang Weijiang 	wrmsr(MSR_IA32_U_CET, ENABLE_SHSTK_BIT);
10779e53994SYang Weijiang 
10879e53994SYang Weijiang 	/* Store shadow-stack pointer. */
10979e53994SYang Weijiang 	wrmsr(MSR_IA32_PL3_SSP, (u64)(shstk_virt + 0x1000));
11079e53994SYang Weijiang 
11179e53994SYang Weijiang 	/* Enable CET master control bit in CR4. */
11279e53994SYang Weijiang 	write_cr4(read_cr4() | X86_CR4_CET);
11379e53994SYang Weijiang 
11496add4abSYang Weijiang 	printf("Unit test for CET user mode...\n");
11596add4abSYang Weijiang 	run_in_user((usermode_func)cet_shstk_func, GP_VECTOR, 0, 0, 0, 0, &rvc);
11679e53994SYang Weijiang 	report(cp_count == 1, "Completed shadow-stack protection test successfully.");
11779e53994SYang Weijiang 	cp_count = 0;
11879e53994SYang Weijiang 
11979e53994SYang Weijiang 	/* Enable indirect-branch tracking */
12079e53994SYang Weijiang 	wrmsr(MSR_IA32_U_CET, ENABLE_IBT_BIT);
12179e53994SYang Weijiang 
12296add4abSYang Weijiang 	run_in_user((usermode_func)cet_ibt_func, GP_VECTOR, 0, 0, 0, 0, &rvc);
12379e53994SYang Weijiang 	report(cp_count == 1, "Completed Indirect-branch tracking test successfully.");
12479e53994SYang Weijiang 
12579e53994SYang Weijiang 	write_cr4(read_cr4() & ~X86_CR4_CET);
12679e53994SYang Weijiang 	wrmsr(MSR_IA32_U_CET, 0);
12779e53994SYang Weijiang 
12879e53994SYang Weijiang 	return report_summary();
12979e53994SYang Weijiang }
130