1 #include "libcflat.h" 2 #include <alloc_page.h> 3 #include "x86/desc.h" 4 #include "x86/processor.h" 5 #include "x86/vm.h" 6 7 volatile int pf_count = 0; 8 volatile int save; 9 volatile unsigned test; 10 11 void do_pf_tss(unsigned long error_code); 12 13 // When doing ring 3 tests, page fault handlers will always run on a 14 // separate stack (the ring 0 stack). Seems easier to use the alt_stack 15 // mechanism for both ring 0 and ring 3. 16 17 void do_pf_tss(unsigned long error_code) 18 { 19 pf_count++; 20 save = test; 21 22 #ifndef __x86_64__ 23 tss[0].eflags |= X86_EFLAGS_AC; 24 #endif 25 } 26 27 extern void pf_tss(void); 28 asm ("pf_tss:\n" 29 #ifdef __x86_64__ 30 // no task on x86_64, save/restore caller-save regs 31 "push %rax; push %rcx; push %rdx; push %rsi; push %rdi\n" 32 "push %r8; push %r9; push %r10; push %r11\n" 33 "mov 9*8(%rsp),%rdi\n" 34 #endif 35 "call do_pf_tss\n" 36 #ifdef __x86_64__ 37 "pop %r11; pop %r10; pop %r9; pop %r8\n" 38 "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n" 39 #endif 40 "add $"S", %"R "sp\n" 41 #ifdef __x86_64__ 42 "orl $(1 << " xstr(X86_EFLAGS_AC_BIT) "), 2*"S"(%"R "sp)\n" // set EFLAGS.AC and retry 43 #endif 44 "iret"W" \n\t" 45 "jmp pf_tss\n\t"); 46 47 48 #define USER_BASE (1 << 23) 49 #define USER_VAR(v) (*((__typeof__(&(v))) (((unsigned long)&v) + USER_BASE))) 50 #define USER_ADDR(v) ((void *)((unsigned long)(&v) + USER_BASE)) 51 52 static void init_test(int i) 53 { 54 pf_count = 0; 55 if (i) { 56 invlpg(&test); 57 invlpg(&USER_VAR(test)); 58 } 59 } 60 61 static void check_smap_nowp(void) 62 { 63 test = 0x99; 64 65 *get_pte(phys_to_virt(read_cr3()), USER_ADDR(test)) &= ~PT_WRITABLE_MASK; 66 67 write_cr4(read_cr4() & ~X86_CR4_SMAP); 68 write_cr0(read_cr0() & ~X86_CR0_WP); 69 clac(); 70 write_cr3(read_cr3()); 71 72 init_test(0); 73 USER_VAR(test) = 0x99; 74 report(pf_count == 0, 75 "write from user page with SMAP=0, AC=0, WP=0, PTE.U=1 && PTE.W=0"); 76 77 write_cr4(read_cr4() | X86_CR4_SMAP); 78 write_cr3(read_cr3()); 79 80 init_test(0); 81 (void)USER_VAR(test); 82 report(pf_count == 1 && save == 0x99, 83 "read from user page with SMAP=1, AC=0, WP=0, PTE.U=1 && PTE.W=0"); 84 85 /* Undo changes */ 86 *get_pte(phys_to_virt(read_cr3()), USER_ADDR(test)) |= PT_WRITABLE_MASK; 87 88 write_cr0(read_cr0() | X86_CR0_WP); 89 write_cr3(read_cr3()); 90 } 91 92 int main(int ac, char **av) 93 { 94 unsigned long i; 95 96 if (!this_cpu_has(X86_FEATURE_SMAP)) { 97 printf("SMAP not enabled\n"); 98 return report_summary(); 99 } 100 101 setup_vm(); 102 setup_alt_stack(); 103 set_intr_alt_stack(14, pf_tss); 104 105 if (reserve_pages(USER_BASE, USER_BASE >> 12)) 106 report_abort("Could not reserve memory"); 107 // Map first 8MB as supervisor pages 108 for (i = 0; i < USER_BASE; i += PAGE_SIZE) { 109 *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~PT_USER_MASK; 110 invlpg((void *)i); 111 } 112 113 // Present the same 8MB as user pages in the 8MB-16MB range 114 for (i = USER_BASE; i < 2 * USER_BASE; i += PAGE_SIZE) { 115 *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~USER_BASE; 116 invlpg((void *)i); 117 } 118 119 clac(); 120 write_cr4(read_cr4() | X86_CR4_SMAP); 121 write_cr3(read_cr3()); 122 123 for (i = 0; i < 2; i++) { 124 if (i) 125 printf("testing with INVLPG\n"); 126 else 127 printf("testing without INVLPG\n"); 128 129 init_test(i); 130 clac(); 131 test = 42; 132 report(pf_count == 0 && test == 42, 133 "write to supervisor page"); 134 135 init_test(i); 136 stac(); 137 (void)USER_VAR(test); 138 report(pf_count == 0, "read from user page with AC=1"); 139 140 init_test(i); 141 clac(); 142 (void)USER_VAR(test); 143 report(pf_count == 1 && save == 42, 144 "read from user page with AC=0"); 145 146 init_test(i); 147 stac(); 148 save = 0; 149 USER_VAR(test) = 43; 150 report(pf_count == 0 && test == 43, 151 "write to user page with AC=1"); 152 153 init_test(i); 154 clac(); 155 USER_VAR(test) = 44; 156 report(pf_count == 1 && test == 44 && save == 43, 157 "read from user page with AC=0"); 158 159 init_test(i); 160 stac(); 161 test = -1; 162 #ifndef __x86_64__ 163 #define TEST "test" 164 #else 165 #define TEST "test(%rip)" 166 #endif 167 asm("or $(" xstr(USER_BASE) "), %"R "sp \n" 168 "push $44 \n " 169 "decl " TEST "\n" 170 "and $~(" xstr(USER_BASE) "), %"R "sp \n" 171 "pop %"R "ax\n" 172 "movl %eax, " TEST "\n"); 173 report(pf_count == 0 && test == 44, 174 "write to user stack with AC=1"); 175 176 init_test(i); 177 clac(); 178 test = -1; 179 asm("or $(" xstr(USER_BASE) "), %"R "sp \n" 180 "push $45 \n " 181 "decl " TEST "\n" 182 "and $~(" xstr(USER_BASE) "), %"R "sp \n" 183 "pop %"R "ax\n" 184 "movl %eax, " TEST "\n"); 185 report(pf_count == 1 && test == 45 && save == -1, 186 "write to user stack with AC=0"); 187 188 /* This would be trapped by SMEP */ 189 init_test(i); 190 clac(); 191 asm("jmp 1f + "xstr(USER_BASE)" \n" 192 "1: jmp 2f - "xstr(USER_BASE)" \n" 193 "2:"); 194 report(pf_count == 0, "executing on user page with AC=0"); 195 } 196 197 check_smap_nowp(); 198 199 // TODO: implicit kernel access from ring 3 (e.g. int) 200 201 return report_summary(); 202 } 203