xref: /kvm-unit-tests/x86/cet.c (revision 79e53994616f5b3aa04a654b585f4f2102564e19)
1*79e53994SYang Weijiang 
2*79e53994SYang Weijiang #include "libcflat.h"
3*79e53994SYang Weijiang #include "x86/desc.h"
4*79e53994SYang Weijiang #include "x86/processor.h"
5*79e53994SYang Weijiang #include "x86/vm.h"
6*79e53994SYang Weijiang #include "x86/msr.h"
7*79e53994SYang Weijiang #include "vmalloc.h"
8*79e53994SYang Weijiang #include "alloc_page.h"
9*79e53994SYang Weijiang #include "fault_test.h"
10*79e53994SYang Weijiang 
11*79e53994SYang Weijiang 
12*79e53994SYang Weijiang static unsigned char user_stack[0x400];
13*79e53994SYang Weijiang static unsigned long rbx, rsi, rdi, rsp, rbp, r8, r9,
14*79e53994SYang Weijiang 		     r10, r11, r12, r13, r14, r15;
15*79e53994SYang Weijiang 
16*79e53994SYang Weijiang static unsigned long expected_rip;
17*79e53994SYang Weijiang static int cp_count;
18*79e53994SYang Weijiang typedef u64 (*cet_test_func)(void);
19*79e53994SYang Weijiang 
20*79e53994SYang Weijiang cet_test_func func;
21*79e53994SYang Weijiang 
22*79e53994SYang Weijiang static u64 cet_shstk_func(void)
23*79e53994SYang Weijiang {
24*79e53994SYang Weijiang 	unsigned long *ret_addr, *ssp;
25*79e53994SYang Weijiang 
26*79e53994SYang Weijiang 	/* rdsspq %rax */
27*79e53994SYang Weijiang 	asm volatile (".byte 0xf3, 0x48, 0x0f, 0x1e, 0xc8" : "=a"(ssp));
28*79e53994SYang Weijiang 
29*79e53994SYang Weijiang 	asm("movq %%rbp,%0" : "=r"(ret_addr));
30*79e53994SYang Weijiang 	printf("The return-address in shadow-stack = 0x%lx, in normal stack = 0x%lx\n",
31*79e53994SYang Weijiang 	       *ssp, *(ret_addr + 1));
32*79e53994SYang Weijiang 
33*79e53994SYang Weijiang 	/*
34*79e53994SYang Weijiang 	 * In below line, it modifies the return address, it'll trigger #CP
35*79e53994SYang Weijiang 	 * while function is returning. The error-code is 0x1, meaning it's
36*79e53994SYang Weijiang 	 * caused by a near RET instruction, and the execution is terminated
37*79e53994SYang Weijiang 	 * when HW detects the violation.
38*79e53994SYang Weijiang 	 */
39*79e53994SYang Weijiang 	printf("Try to temper the return-address, this causes #CP on returning...\n");
40*79e53994SYang Weijiang 	*(ret_addr + 1) = 0xdeaddead;
41*79e53994SYang Weijiang 
42*79e53994SYang Weijiang 	return 0;
43*79e53994SYang Weijiang }
44*79e53994SYang Weijiang 
45*79e53994SYang Weijiang static u64 cet_ibt_func(void)
46*79e53994SYang Weijiang {
47*79e53994SYang Weijiang 	/*
48*79e53994SYang Weijiang 	 * In below assembly code, the first instruction at lable 2 is not
49*79e53994SYang Weijiang 	 * endbr64, it'll trigger #CP with error code 0x3, and the execution
50*79e53994SYang Weijiang 	 * is terminated when HW detects the violation.
51*79e53994SYang Weijiang 	 */
52*79e53994SYang Weijiang 	printf("No endbr64 instruction at jmp target, this triggers #CP...\n");
53*79e53994SYang Weijiang 	asm volatile ("movq $2, %rcx\n"
54*79e53994SYang Weijiang 		      "dec %rcx\n"
55*79e53994SYang Weijiang 		      "leaq 2f, %rax\n"
56*79e53994SYang Weijiang 		      "jmp *%rax \n"
57*79e53994SYang Weijiang 		      "2:\n"
58*79e53994SYang Weijiang 		      "dec %rcx\n");
59*79e53994SYang Weijiang 	return 0;
60*79e53994SYang Weijiang }
61*79e53994SYang Weijiang 
62*79e53994SYang Weijiang void test_func(void);
63*79e53994SYang Weijiang void test_func(void) {
64*79e53994SYang Weijiang 	asm volatile (
65*79e53994SYang Weijiang 			/* IRET into user mode */
66*79e53994SYang Weijiang 			"pushq %[user_ds]\n\t"
67*79e53994SYang Weijiang 			"pushq %[user_stack_top]\n\t"
68*79e53994SYang Weijiang 			"pushfq\n\t"
69*79e53994SYang Weijiang 			"pushq %[user_cs]\n\t"
70*79e53994SYang Weijiang 			"pushq $user_mode\n\t"
71*79e53994SYang Weijiang 			"iretq\n"
72*79e53994SYang Weijiang 
73*79e53994SYang Weijiang 			"user_mode:\n\t"
74*79e53994SYang Weijiang 			"call *%[func]\n\t"
75*79e53994SYang Weijiang 			::
76*79e53994SYang Weijiang 			[func]"m"(func),
77*79e53994SYang Weijiang 			[user_ds]"i"(USER_DS),
78*79e53994SYang Weijiang 			[user_cs]"i"(USER_CS),
79*79e53994SYang Weijiang 			[user_stack_top]"r"(user_stack +
80*79e53994SYang Weijiang 					sizeof(user_stack)));
81*79e53994SYang Weijiang }
82*79e53994SYang Weijiang 
83*79e53994SYang Weijiang #define SAVE_REGS() \
84*79e53994SYang Weijiang 	asm ("movq %%rbx, %0\t\n"  \
85*79e53994SYang Weijiang 	     "movq %%rsi, %1\t\n"  \
86*79e53994SYang Weijiang 	     "movq %%rdi, %2\t\n"  \
87*79e53994SYang Weijiang 	     "movq %%rsp, %3\t\n"  \
88*79e53994SYang Weijiang 	     "movq %%rbp, %4\t\n"  \
89*79e53994SYang Weijiang 	     "movq %%r8, %5\t\n"   \
90*79e53994SYang Weijiang 	     "movq %%r9, %6\t\n"   \
91*79e53994SYang Weijiang 	     "movq %%r10, %7\t\n"  \
92*79e53994SYang Weijiang 	     "movq %%r11, %8\t\n"  \
93*79e53994SYang Weijiang 	     "movq %%r12, %9\t\n"  \
94*79e53994SYang Weijiang 	     "movq %%r13, %10\t\n" \
95*79e53994SYang Weijiang 	     "movq %%r14, %11\t\n" \
96*79e53994SYang Weijiang 	     "movq %%r15, %12\t\n" :: \
97*79e53994SYang Weijiang 	     "m"(rbx), "m"(rsi), "m"(rdi), "m"(rsp), "m"(rbp), \
98*79e53994SYang Weijiang 	     "m"(r8), "m"(r9), "m"(r10),  "m"(r11), "m"(r12),  \
99*79e53994SYang Weijiang 	     "m"(r13), "m"(r14), "m"(r15));
100*79e53994SYang Weijiang 
101*79e53994SYang Weijiang #define RESTOR_REGS() \
102*79e53994SYang Weijiang 	asm ("movq %0, %%rbx\t\n"  \
103*79e53994SYang Weijiang 	     "movq %1, %%rsi\t\n"  \
104*79e53994SYang Weijiang 	     "movq %2, %%rdi\t\n"  \
105*79e53994SYang Weijiang 	     "movq %3, %%rsp\t\n"  \
106*79e53994SYang Weijiang 	     "movq %4, %%rbp\t\n"  \
107*79e53994SYang Weijiang 	     "movq %5, %%r8\t\n"   \
108*79e53994SYang Weijiang 	     "movq %6, %%r9\t\n"   \
109*79e53994SYang Weijiang 	     "movq %7, %%r10\t\n"  \
110*79e53994SYang Weijiang 	     "movq %8, %%r11\t\n"  \
111*79e53994SYang Weijiang 	     "movq %9, %%r12\t\n"  \
112*79e53994SYang Weijiang 	     "movq %10, %%r13\t\n" \
113*79e53994SYang Weijiang 	     "movq %11, %%r14\t\n" \
114*79e53994SYang Weijiang 	     "movq %12, %%r15\t\n" ::\
115*79e53994SYang Weijiang 	     "m"(rbx), "m"(rsi), "m"(rdi), "m"(rsp), "m"(rbp), \
116*79e53994SYang Weijiang 	     "m"(r8), "m"(r9), "m"(r10), "m"(r11), "m"(r12),   \
117*79e53994SYang Weijiang 	     "m"(r13), "m"(r14), "m"(r15));
118*79e53994SYang Weijiang 
119*79e53994SYang Weijiang #define RUN_TEST() \
120*79e53994SYang Weijiang 	do {		\
121*79e53994SYang Weijiang 		SAVE_REGS();    \
122*79e53994SYang Weijiang 		asm volatile ("pushq %%rax\t\n"           \
123*79e53994SYang Weijiang 			      "leaq 1f(%%rip), %%rax\t\n" \
124*79e53994SYang Weijiang 			      "movq %%rax, %0\t\n"        \
125*79e53994SYang Weijiang 			      "popq %%rax\t\n"            \
126*79e53994SYang Weijiang 			      "call test_func\t\n"         \
127*79e53994SYang Weijiang 			      "1:" ::"m"(expected_rip) : "rax", "rdi"); \
128*79e53994SYang Weijiang 		RESTOR_REGS(); \
129*79e53994SYang Weijiang 	} while (0)
130*79e53994SYang Weijiang 
131*79e53994SYang Weijiang #define ENABLE_SHSTK_BIT 0x1
132*79e53994SYang Weijiang #define ENABLE_IBT_BIT   0x4
133*79e53994SYang Weijiang 
134*79e53994SYang Weijiang static void handle_cp(struct ex_regs *regs)
135*79e53994SYang Weijiang {
136*79e53994SYang Weijiang 	cp_count++;
137*79e53994SYang Weijiang 	printf("In #CP exception handler, error_code = 0x%lx\n",
138*79e53994SYang Weijiang 		regs->error_code);
139*79e53994SYang Weijiang 	asm("jmp *%0" :: "m"(expected_rip));
140*79e53994SYang Weijiang }
141*79e53994SYang Weijiang 
142*79e53994SYang Weijiang int main(int ac, char **av)
143*79e53994SYang Weijiang {
144*79e53994SYang Weijiang 	char *shstk_virt;
145*79e53994SYang Weijiang 	unsigned long shstk_phys;
146*79e53994SYang Weijiang 	unsigned long *ptep;
147*79e53994SYang Weijiang 	pteval_t pte = 0;
148*79e53994SYang Weijiang 
149*79e53994SYang Weijiang 	cp_count = 0;
150*79e53994SYang Weijiang 	if (!this_cpu_has(X86_FEATURE_SHSTK)) {
151*79e53994SYang Weijiang 		printf("SHSTK not enabled\n");
152*79e53994SYang Weijiang 		return report_summary();
153*79e53994SYang Weijiang 	}
154*79e53994SYang Weijiang 
155*79e53994SYang Weijiang 	if (!this_cpu_has(X86_FEATURE_IBT)) {
156*79e53994SYang Weijiang 		printf("IBT not enabled\n");
157*79e53994SYang Weijiang 		return report_summary();
158*79e53994SYang Weijiang 	}
159*79e53994SYang Weijiang 
160*79e53994SYang Weijiang 	setup_vm();
161*79e53994SYang Weijiang 	setup_idt();
162*79e53994SYang Weijiang 	handle_exception(21, handle_cp);
163*79e53994SYang Weijiang 
164*79e53994SYang Weijiang 	/* Allocate one page for shadow-stack. */
165*79e53994SYang Weijiang 	shstk_virt = alloc_vpage();
166*79e53994SYang Weijiang 	shstk_phys = (unsigned long)virt_to_phys(alloc_page());
167*79e53994SYang Weijiang 
168*79e53994SYang Weijiang 	/* Install the new page. */
169*79e53994SYang Weijiang 	pte = shstk_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK;
170*79e53994SYang Weijiang 	install_pte(current_page_table(), 1, shstk_virt, pte, 0);
171*79e53994SYang Weijiang 	memset(shstk_virt, 0x0, PAGE_SIZE);
172*79e53994SYang Weijiang 
173*79e53994SYang Weijiang 	/* Mark it as shadow-stack page. */
174*79e53994SYang Weijiang 	ptep = get_pte_level(current_page_table(), shstk_virt, 1);
175*79e53994SYang Weijiang 	*ptep &= ~PT_WRITABLE_MASK;
176*79e53994SYang Weijiang 	*ptep |= PT_DIRTY_MASK;
177*79e53994SYang Weijiang 
178*79e53994SYang Weijiang 	/* Flush the paging cache. */
179*79e53994SYang Weijiang 	invlpg((void *)shstk_phys);
180*79e53994SYang Weijiang 
181*79e53994SYang Weijiang 	/* Enable shadow-stack protection */
182*79e53994SYang Weijiang 	wrmsr(MSR_IA32_U_CET, ENABLE_SHSTK_BIT);
183*79e53994SYang Weijiang 
184*79e53994SYang Weijiang 	/* Store shadow-stack pointer. */
185*79e53994SYang Weijiang 	wrmsr(MSR_IA32_PL3_SSP, (u64)(shstk_virt + 0x1000));
186*79e53994SYang Weijiang 
187*79e53994SYang Weijiang 	/* Enable CET master control bit in CR4. */
188*79e53994SYang Weijiang 	write_cr4(read_cr4() | X86_CR4_CET);
189*79e53994SYang Weijiang 
190*79e53994SYang Weijiang 	func = cet_shstk_func;
191*79e53994SYang Weijiang 	RUN_TEST();
192*79e53994SYang Weijiang 	report(cp_count == 1, "Completed shadow-stack protection test successfully.");
193*79e53994SYang Weijiang 	cp_count = 0;
194*79e53994SYang Weijiang 
195*79e53994SYang Weijiang 	/* Do user-mode indirect-branch-tracking test.*/
196*79e53994SYang Weijiang 	func = cet_ibt_func;
197*79e53994SYang Weijiang 	/* Enable indirect-branch tracking */
198*79e53994SYang Weijiang 	wrmsr(MSR_IA32_U_CET, ENABLE_IBT_BIT);
199*79e53994SYang Weijiang 
200*79e53994SYang Weijiang 	RUN_TEST();
201*79e53994SYang Weijiang 	report(cp_count == 1, "Completed Indirect-branch tracking test successfully.");
202*79e53994SYang Weijiang 
203*79e53994SYang Weijiang 	write_cr4(read_cr4() & ~X86_CR4_CET);
204*79e53994SYang Weijiang 	wrmsr(MSR_IA32_U_CET, 0);
205*79e53994SYang Weijiang 
206*79e53994SYang Weijiang 	return report_summary();
207*79e53994SYang Weijiang }
208