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 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 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 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 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(21, 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