1
2 #include "libcflat.h"
3 #include "x86/desc.h"
4 #include "x86/processor.h"
5 #include "x86/vm.h"
6 #include "x86/msr.h"
7 #include "vmalloc.h"
8 #include "alloc_page.h"
9 #include "fault_test.h"
10
11 static int cp_count;
12 static unsigned long invalid_offset = 0xffffffffffffff;
13
cet_shstk_func(void)14 static u64 cet_shstk_func(void)
15 {
16 unsigned long *ret_addr, *ssp;
17
18 /* rdsspq %rax */
19 asm volatile (".byte 0xf3, 0x48, 0x0f, 0x1e, 0xc8" : "=a"(ssp));
20
21 asm("movq %%rbp,%0" : "=r"(ret_addr));
22 printf("The return-address in shadow-stack = 0x%lx, in normal stack = 0x%lx\n",
23 *ssp, *(ret_addr + 1));
24
25 /*
26 * In below line, it modifies the return address, it'll trigger #CP
27 * while function is returning. The error-code is 0x1, meaning it's
28 * caused by a near RET instruction, and the execution is terminated
29 * when HW detects the violation.
30 */
31 printf("Try to temper the return-address, this causes #CP on returning...\n");
32 *(ret_addr + 1) = 0xdeaddead;
33
34 return 0;
35 }
36
cet_ibt_func(void)37 static u64 cet_ibt_func(void)
38 {
39 /*
40 * In below assembly code, the first instruction at label 2 is not
41 * endbr64, it'll trigger #CP with error code 0x3, and the execution
42 * is terminated when HW detects the violation.
43 */
44 printf("No endbr64 instruction at jmp target, this triggers #CP...\n");
45 asm volatile ("movq $2, %rcx\n"
46 "dec %rcx\n"
47 "leaq 2f(%rip), %rax\n"
48 "jmp *%rax \n"
49 "2:\n"
50 "dec %rcx\n");
51 return 0;
52 }
53
54 #define ENABLE_SHSTK_BIT 0x1
55 #define ENABLE_IBT_BIT 0x4
56
handle_cp(struct ex_regs * regs)57 static void handle_cp(struct ex_regs *regs)
58 {
59 cp_count++;
60 printf("In #CP exception handler, error_code = 0x%lx\n",
61 regs->error_code);
62 /* Below jmp is expected to trigger #GP */
63 asm("jmpq *%0": :"m"(invalid_offset));
64 }
65
main(int ac,char ** av)66 int main(int ac, char **av)
67 {
68 char *shstk_virt;
69 unsigned long shstk_phys;
70 unsigned long *ptep;
71 pteval_t pte = 0;
72 bool rvc;
73
74 cp_count = 0;
75 if (!this_cpu_has(X86_FEATURE_SHSTK)) {
76 printf("SHSTK not enabled\n");
77 return report_summary();
78 }
79
80 if (!this_cpu_has(X86_FEATURE_IBT)) {
81 printf("IBT not enabled\n");
82 return report_summary();
83 }
84
85 setup_vm();
86 handle_exception(CP_VECTOR, handle_cp);
87
88 /* Allocate one page for shadow-stack. */
89 shstk_virt = alloc_vpage();
90 shstk_phys = (unsigned long)virt_to_phys(alloc_page());
91
92 /* Install the new page. */
93 pte = shstk_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK;
94 install_pte(current_page_table(), 1, shstk_virt, pte, 0);
95 memset(shstk_virt, 0x0, PAGE_SIZE);
96
97 /* Mark it as shadow-stack page. */
98 ptep = get_pte_level(current_page_table(), shstk_virt, 1);
99 *ptep &= ~PT_WRITABLE_MASK;
100 *ptep |= PT_DIRTY_MASK;
101
102 /* Flush the paging cache. */
103 invlpg((void *)shstk_phys);
104
105 /* Enable shadow-stack protection */
106 wrmsr(MSR_IA32_U_CET, ENABLE_SHSTK_BIT);
107
108 /* Store shadow-stack pointer. */
109 wrmsr(MSR_IA32_PL3_SSP, (u64)(shstk_virt + 0x1000));
110
111 /* Enable CET master control bit in CR4. */
112 write_cr4(read_cr4() | X86_CR4_CET);
113
114 printf("Unit test for CET user mode...\n");
115 run_in_user((usermode_func)cet_shstk_func, GP_VECTOR, 0, 0, 0, 0, &rvc);
116 report(cp_count == 1, "Completed shadow-stack protection test successfully.");
117 cp_count = 0;
118
119 /* Enable indirect-branch tracking */
120 wrmsr(MSR_IA32_U_CET, ENABLE_IBT_BIT);
121
122 run_in_user((usermode_func)cet_ibt_func, GP_VECTOR, 0, 0, 0, 0, &rvc);
123 report(cp_count == 1, "Completed Indirect-branch tracking test successfully.");
124
125 write_cr4(read_cr4() & ~X86_CR4_CET);
126 wrmsr(MSR_IA32_U_CET, 0);
127
128 return report_summary();
129 }
130