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